GC|深入理解JVM底层实现_2 对象结构、指针引用和垃圾回收机制

深入理解JVM底层实现 2 对象结构、指针引用和垃圾回收机制 田超凡
2019-11-07
转载请注明原作者
1 对象的结构
堆区中的对象结构有以下几个基本组成单位:
(1).对象头(Object Header,OH):存放对象的标识信息和敏感信息
【GC|深入理解JVM底层实现_2 对象结构、指针引用和垃圾回收机制】(2).类型指针(Type Pointer,TP):指向运行时常量池中对象对应类型的符号,同一个类型的不同对象类型指针指向同一个内存地址,表示当前对象指向哪个class类型对象,对应调用的方法也是调用class类型对象中的方法
(3).数组长度(Array Length,AL):只有数组类型的对象才会拥有这个组成部分
(4).对象数据(Object Data,OD):主要用来存放对象自身的属性和变量(包括继承关系下父类的属性)等对象的基本信息,主要包括以下几个类型的对象数据:
哈希值、GC分代年龄(young/old)、锁状态标志、线程持有的锁
注意,GC分代的好处是可以根据对象在堆中所处的区域的不同,执行不同的垃圾回收算法
(5).填充数据(Padding Data,PD):使用符号或二进制等数据填充占位,只是为了达到分配的对象大小,没有实际的意义,不同虚拟机分配的对象大小有所不同,比如HotStop 虚拟机指定对象大小必须是8个字节的整数倍。如果不是8个字节则使用此规则进行对象数据填充

2 对象的访问方式-指针引用
堆区中存放的对象都是通过线程独占区的栈区的JVM指针根据对象在堆中存放的内存地址进行访问的,这个过程叫做对象引用,也叫指针引用。
指针引用在不同JVM虚拟机上实现的策略是不同的,举例说明如下:

2.1 直接引用
JVM虚拟机、HotSpot虚拟机的默认对象引用方式,栈中的指针直接通过给对象分配内存时获取的对象内存地址进行访问,如果对象在堆中重新分配了内存地址,那么栈中的指针指向的原来的内存地址就会失效,必须重新建立对重新分配后的对象所在内存地址的引用。
优点:栈直接通过指针访问堆中的对象,执行效率高,速度比较快
缺点:对象重新分配内存地址后必须重新建立直接引用
GC|深入理解JVM底层实现_2 对象结构、指针引用和垃圾回收机制
文章图片


2.2 句柄引用
Alibaba虚拟机采用的对象引用方式。句柄引用通过在堆中分配一块固定的空间(句柄池)来存放已经分配内存的对象内存地址,栈直接根据对象标识符访问句柄池后,即可通过句柄池中存放的对象内存地址实现对象访问,当对象重新分配内存空间之后,句柄池中的对象内存地址会实时监听并更新,但是对于栈而言指针只需要访问句柄池即可,因此不会改变指针引用。
优点:当对象的内存地址发生变化时,只需要更新句柄池中的内存地址即可,不需要改变栈中原来已经建立的指针引用。
缺点:栈每次访问对象必须通过句柄池,多了一个环节,效率和速度降低。
综上所述不难看出,直接引用和句柄引用在优缺点上是互补的,可以说是各有千秋,目前开发Java程序用的都是默认的JVM虚拟机,所以一般都采用的是直接引用的方式来根据内存地址访问堆中的对象。
GC|深入理解JVM底层实现_2 对象结构、指针引用和垃圾回收机制
文章图片


3 对象容器池-堆(Heap)的内存结构
线程共享区的堆区用来存放和管理所有在应用程序运行周期内创建的对象实例,提供对象引用供每个线程的栈访问。堆区存放的对象实例在所有线程中都是共享的、可访问和操作的。
堆区中的对象存放在两大块中,分别是:新生代(younger)和老年代(older)。
新生代(younger)主要存放的是瞬时状态的对象,可以简单理解为存放的是生命周期短暂的,非永久长期存在的对象。新生代中存放的对象又分为eden(伊甸园区)和survivor(幸存者区)两个部分,新创建的对象默认存放在新生代的eden伊甸园区,复制算法会在eden伊甸园区和survivor幸存者区之间实现对象批量拷贝,进而监控对象整个运行期间的生命周期。
老年代(older)存放的是整个应用程序运行期间长期或者永久存在的对象,对于新生代中经过复制算法批量拷贝后仍然存活的对象会直接放到老年代中。
GC|深入理解JVM底层实现_2 对象结构、指针引用和垃圾回收机制
文章图片


4 垃圾回收机制概述(GC)
垃圾回收机制指的是JVM虚拟机会不定时去堆内存中清理不可达对象。不可达的对象并不会马上就会直接回收,垃圾回收器在一个Java程序中的执行是自动的,不能强制执行,即使程序员能明确地判断出有一块内存已经无用了,是应该回收的,程序员也不能强制垃圾收集器回收该内存块。程序员唯一能做的就是通过调用System.gc 方法来"建议"执行垃圾收集器,但其是否可以执行,什么时候执行却都是不可知的。这也是垃圾收集器的最主要的缺点。当然相对于它给程序员带来的巨大方便性而言,这个缺点是瑕不掩瑜的。
(1) System.gc()方法可以告诉JVM虚拟机,建议你可以进行GC垃圾回收了,但是此时JVM到底有没有真正执行内存回收,什么时候执行的内存回收我们是无法知道和强制干预的,这个方法只是显示告诉JVM一种回收建议,相比较不使用本方法直接采用JVM默认的定时垃圾回收机制,此方法可能会在调用时加大JVM虚拟机内存回收权重和优先级,因为JVM垃圾回收机制真正的底层实现是调用基于C语言编写的原生方法(native)直接操作系统的内存缓冲区。
(2) finalize()方法定义在Object类中,finalize方法会在重写该方法的类中手动调用System.gc()方法建议垃圾回收器回收堆内死亡对象之前调用。

public class Test {
public static void main(String[] args) {
Test test = new Test();
test = null;
System.gc(); // 手动回收垃圾
}

@Override
protected void finalize() throws Throwable {
// gc回收垃圾之前调用
System.out.println("垃圾回收机制...");
}
}


转载请注明原作者

    推荐阅读