• java内存区域及溢出异常


    内存划分:

    java虚拟机在执行java程序过程中会把内存分为以下区域进行管理

    线程私有的
      虚拟机栈
        局部变量表
          基本数据类型
            long和double占用两个slot
          对象引用
          返回地址

        操作数栈
        动态链接
        方法出口等信息

        抛出异常:

          栈深度过大 StackOverflowError
          申请内存空间不足 OutOfMemoryError

      程序计数器

      本地方法栈
    线程共享的
      堆
        虚拟机启东时创建
      方法区
        常量池的回收和类型的卸载
        运行常量池:字面量和符号引用 翻译出来的直接引用

      直接内存

        NIO可以使用Native函数库直接分配,这样能显著增加效率,因为不用在java堆和native堆中复制数据

        然而,分配内存时如果直接内存加java内存超过计算机物理内存限制,就会报出OutOfMemoryError

    虚拟机对象:
    对象的创建:
      1.new定义到常量池中的一个符号
      2.检查符号代表的类是否被装载 解析 初始化
        如果没有,加载类
      3.加载完类后,为新生对象分配内存(类加载后会确定分配的内存大小)

        内存分配方法:
          如果内存是规整的 指针碰撞(指针移动对象的内存大小的位置)
          如果内存是交错的 空闲列表 列表记录哪些内存是可用的,分配对象实例时分配给足够大的内存给对象,并更新列表

          选择哪种分配方法由java堆是否规整来决定,堆是否规整由java回收器是否采用压缩整理功能决定


        分配内存的线程安全问题
          对分配内存的动作进行同步处理
          TLAB 本地分配缓冲区 每个线程预先分配一块内存,哪个线程要分配内存,就在哪个线程上面分配,只有在TLAB用尽并分配新的TLAB时才需要同步锁
      4.内存分配之后,内存空间都初始化为零值
      5.对对象进行必要设置,设置对象头(类 元数据 hashcode gc年代)

    对象的布局:
      对象头
        运行时数据
          HashCode GC分代年龄 锁状态标识 线程持有的锁 偏向线程id 偏向时间戳
        类型指针
          如果是数组,对象头要保存记录数组的长度信息
      实例数据
        虚拟机分配策略
      对齐填充
        hotpot虚拟机需要对象的大小是8个字节的整数倍 对象头正好是8个字节的倍数 如果实例数据没有对齐时,需要用对齐填充来补充


    对象的访问定位:
      引用定义在栈上,虚拟机规范没有定义引用以何种方式定位对象位置,由虚拟机自身来完成,主流访问方式两种:
      1.句柄

        

        好处:对象移动时,只需要改变句柄的实例数据指针,不需要改变栈中本地变量的指针(它指向)

      2.直接指针

        

        好处 速度更快,节省一次指针定位的时间

    OutOfMemoryError
    java堆溢出:

      通过-XX:+HeapDumpOnOutOfMemoryError可以在虚拟机异常时候dump出当前堆转储快照以便事后分析
      VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOutOfMemoryError

      MAT Tool插件安装

      内存转存储分析工具的Eclipse Memory Analyzer的使用

    /**
     * 
     */
    package com.gengsc.oom;
    
    /**
     * VM args:-Xss2M
     * 
     * @author shichaogeng
     *
     * 2017年6月26日
     */
    public class JavaVMOOM {
    
        private void dontStop() {
            while (true) {
                //...
            }
        }
        
        public void StackLeakByThread() {
            while (true) {
                Thread thread = new Thread(new Runnable() {
                    public void run() {
                        dontStop();
                    }
                });
                thread.start();
            }
        }
        
        public static void main(String[] args) {
            JavaVMOOM oom = new JavaVMOOM();
            oom.StackLeakByThread();
        }
    }

    通过不断的建立线程可以导致OutOfMemoryError

    这种异常很奇特,为每个线程分配的内存越大,反而越容易产生内存溢出

    这是什么原因呢,操作系统为每个进程分配内存是有限制的,减去Xms,减去MaxPermSize,剩下的就分给虚拟机栈和本地方法栈了,每个线程分配内存越大,线程数量越少,越易产生内存溢出异常

    正确的处理方式就是减少java堆的大小和栈内存来换取线程数量。

    方法区域和运行时常量池内存溢出:

    String.intern()方法:当常量池中存在string时,返回这个字符串对象,不存在时,添加到常量池并返回字符串的引用。

    关于String#intern()的详细内容可以点我

  • 相关阅读:
    接口问题
    鉴权 授权 验签
    adb常用命令
    cookie session
    常见http返回状态码
    Linux下mysql数据库的命令
    Linux课堂笔记--第九天
    Linux课堂随笔 -第八天
    Linux课堂笔记-第七天
    Linux课堂随笔-第六天
  • 原文地址:https://www.cnblogs.com/gengsc/p/7079532.html
Copyright © 2020-2023  润新知