天天看点

浅谈Java虚拟机JVM的垃圾回收机制

1. 什么是垃圾

要回收垃圾,那么垃圾是什么?简单的逻辑就是不会再被使用的内存对象呗。

2. 怎么判断不再被使用

2.1 引用计数法。统计有多少个引用指向内存对象,如果没有引用指向内存对象,那么该内存对象可以被当做垃圾。但是这边有一个循环引用的问题,如一个方法内,创建了两个新节点,它们之间互相引用,最终方法执行结束,方法内引用的对象应该被回收,但如果使用引用计数方案来处理,由于节点有被引用,会造成无法回收。

浅谈Java虚拟机JVM的垃圾回收机制

2.2 可达性分析。如果一个对象和GC Roots之间没有链路连接,那么该对象可回收。但为了避免可达性分析过程中,其它用户线程更改对象引用关系,会有GC停顿操作,也就是暂停Java所有工作线程。各个线程通过主动式查询中断标志位,然后决定是否中断停在附近的安全点(方法调用、循环跳转这些具有让程序长时间执行的指令会产生安全点)。因为需要线程主动查询中断标志位,如果是那些睡眠或阻塞的线程,它们无法主动去查询中断标志位,因此它们会在进入睡眠或阻塞这些安全区域代码时,标识自己进入了安全区域。当虚拟机发起垃圾回收时不需要管进入安全区域的线程,如果这些线程在垃圾回收时结束了安全区域代码,那么会等待,直到JVM完成GC Roots枚举。

2.3 GC Roots对象。可作为GC Roots对象的有四类:①虚拟机栈中局部变量表引用的对象;②类静态属性引用的对象;③方法区中常量引用的对象;④本地方法栈(带有native修饰符的方法)中引用的对象。

3. 回收机制

确定了垃圾后,回收的机制:①如果“垃圾”对象没有重写finalize方法或者已经调用过finalize方法,那么该“垃圾”对象直接被回收;②如果“垃圾”对象重写了finalize方法并且还没有调用过finalize方法,那么会被放入F-Queue队列中;稍后虚拟机会创建Finalize线程并执行finalize方法,如果对象要“拯救”自己,那么只要在finalize方法中再和GC Roots引用链上任何一个对象连接上即可。如果对象这时候还没有逃脱,那么它就真的被回收了。

4. 回收方法区(也叫永久代)的废弃常量和无用的类

虽然虚拟机规范不要求对方法区进行垃圾回收,但还是可以对废弃常量和无用类进行回收。无用类的判断条件:①任何实例都被回收;②加载该类的ClassLoad也被回收;③该类对应的java.lang.Class对象没有在任何地方被引用。