美文网首页
Java对象详解-堆内存具体分配及回收

Java对象详解-堆内存具体分配及回收

作者: 梦想做一个不秃头的程序猿 | 来源:发表于2019-08-14 20:32 被阅读0次

\color{#FF1493}{1. 对象创建过程概括}

  • 普通java对象创建(不包括数组及Class对象)
      对象的创建对应于虚拟机执行一条new指令这一过程。在执行new指令时,虚拟机会做出如下操作:
        1. 检查参数是否可以被定位到常量池中的一个类的符号引用C。
        2. 将此C所代表的类是否已被加载(此步可以确认对象所需内存大小),解析,初始化。如果没有则先进行类加载;如果已经完成,则转到步骤三。
        3. 虚拟机为新生对象分配内存(有指针碰撞、空闲列表两种方式)并将分配到的内存空间都初始化为零值。
        4. 虚拟机根据对象头中的信息对对象进行设置,确定对象属于哪个类,如何才能找到类元信息,对象的哈希码,对象的GC分代年龄等信息。至此,虚拟机角度一个新的对象已经产生。但是此时所有字段还都为零值。在new指令之后会接着执行<init>方法,把对象按找程序员设定的值进行初始化,至此才算产生一个完整的java对象。

\color{#FF1493}{2. 类加载过程}

  类从被加载到虚拟机开始,到卸载出内存,主要包括:加载、验证、准备、解析、初始化、使用、卸载七个阶段。其中验证、准备、解析三个阶段统称为连接。而类加载的整个过程具体包括:加载、验证、准备、解析、初始化这五个阶段的动作。

  1. 加载
      加载是类加载的一个阶段,具体完成以下动作:
        1. 通过一个类的全限定名来获取该类的二进制字节流。
        2. 将这个字节流所代表的静态存储结构转换为方法区运行时数据结构。
        3. 在内存中生存一个java.lang.Class对象,作为方法区这个类的各种数据访问入口。
  2. 验证
      检查二进制流的合法性
  3. 准备
      正式为类变量分配内存并设置初始值(一般为数据类型的零值)。注意只有类变量,实例变量会在对象实例化时一起被分配到堆中。而这些类变量都位于方法区。
      public static int value = 1; 准备阶段过后value的值为0。
  4. 解析
      虚拟机将常量池中的符号引用替换为直接引用。解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用限定点符7类符号引用进行。
  5. 初始化
      类加载过程的最后一步,对应于执行类构造器<clinit>()方法的过程, 该方法包括所有编译器自动收集的类中静态变量的赋值及静态代码块中的语句。在子类的<clinit>()方法调用之前,虚拟机栈保证一定会先调用父类的<clinit>()。同一个类加载器,一个类型只会初始化一次。
  • 虚拟机规范规定仅在五种情形下必须立即对类进行初始化:
  1. 遇到new、getstatic、putstatic、invokestatic字节指令时。

生成这四个指令最常见的情况是:使用new指令创建对象、读取或设置一个静态字段(被 final 修饰、已在编译期把结果放入常量池的静态字段除外)、调用类的静态方法时。

  1. 反射。
  2. 初始化子类,如果父类没有进行过初始化,则会触发父类初始化。
  3. 虚拟机启动时,会将main入口方法所在类初始化。
  4. JDK 1.7的动态语言支持,方法句柄所对应的类未初始化。
  • 不会进行类初始化的情形:
  1. 子类引用父类静态字段,不会引起子类的初始化。
  2. 通过数组定义来引用类,不会触发类的初始化。
  3. 引用一个类常量不会触发它的初始化。

\color{#FF1493}{3. 对象内存布局}

  对象在内存中的存储布局分为:对象头、实例数据,对齐填充三部分。

  • 对象头(包括两部分):
    (1)Mark Word。存储运行时数据,包括GC年龄分代、锁状态标志、偏向线程ID等。
    (2)类型指针。对象指向它的类元数据的指针,用以确定这个对象属于哪个类。
    (*)对于数组对象,在对象头中还要额外开辟一块内存用于存储数组长度。
  • 实例数据:
      这部分时对象真正的有效信息,存储着代码中定义的所有字段内容,包括其从父类继承的字段。
  • 对齐数据:
      保证对象其实地址是8的整数倍。

\color{#FF1493}{4. 内存分配与回收}

  对象主要分配在Eden区,如果启动了本地线程分配缓冲,将按线程优先在TLAB上分配,有时也会直接分配到老年代。分配的规则并不固定,取决于使用了哪一种垃圾回收器组合及虚拟机中内存相关参数的设定。
  大致有以下几种普遍规则:

  1. 对象优先在Eden区分配,如果Eden区域大小足够,就直接分一块内存。否则会触发一次Minor GC。
      在触发Minor GC前会检查老年代剩余的最大连续空间(设大小为X)是否大于新生代中所有对象所占空间的总和,如果大于则保证Minor GC是安全的,可直接进行Minor GC。否则进一步判断是否支持担保失败,如果支持并且X的大小>历次晋升到老年代对象的和平均值,则先试着进行一次Minor GC,否则先进行一次Full GC。
  2. 如果为大对象(大对象即需要大量连续存储空间的对象,如很长的字符串或数组),当其大小达到一个阈值(-XX:PretenureSizeThreshold)时会被直接分配到老年代。以避免新生代区域内部之间发生大量的内存复制。
  3. 对于长久存活的对象将进入老年代。对象每熬过依次Minor GC就会将其对象头内的年龄计数器加一,到达一定阈值(默认15,-XX :MaxTenuringThr eshold)就会晋升到老年代。
  4. 动态年龄判定。如果在Survivor空间相同年龄的所有对象的大小的总和大于Survivor区域空间的一半,那么年龄大于或者等于该年龄的对象也会被移入老年代,无需等到达到(3)中的阈值。

\color{red}{学习参考书:}
《深入理解java虚拟机》

相关文章

  • Java对象详解-堆内存具体分配及回收

    普通java对象创建(不包括数组及Class对象)  对象的创建对应于虚拟机执行一条new指令这一过程。在执行ne...

  • Java虚拟机内存分配与回收策略

    Java虚拟机中的内存分配与回收策略就是 Java的自动内存管理,其最核心的部分就是堆内存中对象的分配与回收。所以...

  • 实例变量与类变量

    java内存管理分为两个方面:内存分配和内存回收,这里的内存分配是指创建java对象时jvm为该对像在堆内存中分配...

  • android GC 笔记

    回收哪里的内存 栈的内存管理是顺序分配的,而且定长,不存在内存回收问题;而堆 则是为java对象的实例随机分配内存...

  • Java中四种引用

    Java内存管理包括内存分配和内存回收。 内存分配:程序员通过new对象,JVM会自动为该对象分配内存。 内存回收...

  • Java编程语言:java中四种引用!欢迎补充

    Java内存管理包括内存分配和内存回收。 内存分配:程序员通过new对象,JVM会自动为该对象分配内存。 内存回收...

  • Java之JVM对象分配

    分配流程 栈上分配 出现原因 Java堆中内存是线程共享的,假设所有对象都从堆中分配的话,所有回收对象的筛选、整理...

  • Java内存回收机制--Java引用的种类(强引用、弱引用、软引

    Java内存管理包括内存分配(创建Java对象)和内存回收(回收Java对象)。这两者都是JVM(Java虚拟机)...

  • JVM编译器优化(1)

    栈上分配 1. 出现原因 Java堆中内存是线程共享的,假设所有对象都从堆中分配的话,所有回收对象的筛选、整理、清...

  • 内存分配与回收

    概述 java技术体系所提倡的自动内存管理其实主要包括 ①:给对象分配内存 ②:回收给对象分配的内存对象的内存分配...

网友评论

      本文标题:Java对象详解-堆内存具体分配及回收

      本文链接:https://www.haomeiwen.com/subject/rtpvjctx.html