一、对象在堆内存中的布局
(一)对象头(Object Header)
1、MarkWord
(1)HashCode、分代年龄、是否偏向锁、锁标记位;
(2)64位虚拟机占用8byte;
2、ClassMetadataAddress/KlassWord
(1)类型指针指向对象的类元数据,JVM通过这个指针确定该对象是哪个类的实例;
(2)64位虚拟机占用8byte,开启指针压缩占用4byte;
3、ArrayLenght
(1)如果是数组对象,存储数组长度,非数组对象不存在;
(2)64位虚拟机占用4byte;
(二)实例数据(Instance Data)
当前对象属性成员数据以及父类属性成员数据;
(三)对齐填充
1、目的
为了方便内存的单元读取、寻址、分配,Java对象的总大小必须要为8的整数倍;
2、方式
当一个对象的对象头和实例数据大小不为8的整数倍时,就会将对象大小补齐为8的整数倍;
二、创建对象的内存分配过程
(一)创建对象的方式
1、正式
通过new关键字创建;
2、其他
(1)调用Class类的newInstance方法完成对象创建;
(2)调用Constructor类的newInstance方法完成创建;
(3)实现Cloneable接口,通过clone方法克隆对象完成创建;
(4)从本地文件、网络中读取二进制流数据,通过反序列化完成创建;
(5)使用第三方库完成对象创建;
(二)创建过程
1、类加载检测
(1)首先去检查这个创建指令的参数是否能在常量池中定位到一个类的符号引用;
(2)同时检查这个符号引用代表的类是否被加载解析初始化过;
(3)如果没有被加载,执行类加载子系统的加载过程;
2、内存分配
(1)计算出该对象所需的内存空间大小(对象头 + 实例数据 + 对齐填充);
(2)在内存中划出一块与对象大小相等的区域出来,然后将对象放进去;
栈上分配:属于C2编译器的激进优化;
TLAB分配:TLAB全称Thread Local Allocation Buffer,是指JVM在Eden区为每条线程划分的一块私有缓冲内存;
老年代分配:初次分配时,大对象直接进入年老代;
新生代分配:指针碰撞(Serial、ParNew)、空闲列表(CMS);
3、初始化内存
(1)JVM会将分配到的内存空间(不包括对象头)都初始化为零值;
(2)可以保证对象的实例字段在Java代码中不赋初始值就直接使用,避免空指针异常;
4、设置对象头
(1)将对象的原始哈希码、GC年龄、锁标志、锁信息组装成MrakWord放入对象头中;
(2)将指向当前对象类元数据的类型指针KlassWord加入对象头中;
(3)如果当前对象是数组对象,将编码时指定的数组长度ArrayLength放入对象头中;
5、执行<init>函数
构造函数,主要是对属性进行显式赋值;
三、对象的使用和回收
(一)对象的引用类型
1、强引用(StrongReference)
(1)通过new指令创建出来的对象都属于强引用类型,堆中的对象与栈中的变量保持着直接引用;
(2)堆中的对象存在强引用时,GC机制宁愿抛出OOM也不会强制回收;
(3)如果确定一个对象不再使用后,可以显示的将对象引用清空,如:obj = null;;
2、软引用(SoftReference)
(1)在堆内存不足的情况下,该引用级别的对象将被GC机制回收;
(2)可以实现JVM级别的简单缓存;
3、弱引用(WeakReference)
(1)发生GC时,不管当前的堆内存资源是否紧张,都会被回收;
(2)用于保存那些可有可无的缓存数据,内存充足时可以稍微增加程序的执行效率;
4、虚引用(PhantomReference)
(1)GC机制将会把虚引用当成一个没有任何引用类型的对象,随时随刻可以回收;
(2)需要配合ReferenceQueue引用队列使用;
(3)GC机制会在回收前,把虚引用加入到与之关联的引用队列中;
(4)可用于跟踪垃圾回收过程,也可以将一些资源释放操作放置在虚引用中执行和记录;
(二)内存中对象的访问方式
1、句柄访问
(1)对象被移动(GC时会发生)时只改变句柄中实例数据指针,reference本身不用改变;
(2)每次访问对象时都需要经过一次转发,访问速度会比直接指针方式慢;
2、直接指针访问(HotSpot虚拟机)
(1)速度快,节省了一次指针定位的时间开销;
(2)当GC发生对象移动时,被移动的对象对应的所有reference中的引用信息也需要同步更新;
(三)GC时的对象
1、对象经历过程
(1)发生GC时,Eden存活的对象会转移到Survivor(To),Survivor(From)中的对象也会转移到Survivor(To),新生代剩余对象被回收,Survivor(To)变成Survivor(From);
(2)当对象移动一次,那么对象头内MrakWord中的对象年龄则会加1;
(3)当对象年龄达到老年代年龄判定条件时,对象从Survivor转移到老年代;
2、对象年龄判定
(1)晋升标准默认为15岁(CMS为8岁),也就是当对象来回移动16次之后;
(2)动态对象年龄判定:如果在Survivor区中相同年龄的所有对象大小总和大于Survivor空间的一半,那么Survivor区中所有大于或等于该年龄的对象就可以直接进入年老代;
(3)大对象被创建时,直接进入老年代;
3、分配担保机制
(1)当发生GC时,一个Survivor区空间无法储存Eden区和另外一个Survivor区的存活对象时,这些对象会被直接转移到年老代;
(2)在进行MinorGC前,如果老年代的连续空间大于新生代对象大小总和或历次晋升的平均大小,则此次MinorGC是安全的,则进行MinorGC,否则进行FullGC;