理解 JVM 工作机制(四) 回收策略
回收策略
程序计数器、虚拟机栈、本地方法栈随线程而生,也随线程而灭;栈帧随着方法的开始而入栈,随着方法的结束而出栈。这几个区域的内存分配和回收都具有确定性,在这几个区域内不需要过多考虑回收的问题,因为方法结束或者线程结束时,内存自然就跟随着回收了。
由于堆和方法区的分配内存具有不确定性。所以,垃圾收集器着重于在这两个地方进行内存的回收。
如何知道对象不再被使用?
若一个对象不被任何对象或变量引用,那么它就是无效对象,需要被回收。
引用计数法
在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一。
缺点:引用计数并不可靠。因为如果两个对象的两个字段互相引用,即使对象无法访问,引用计数也都不为零。算法就无法回收它们。
但在 Java 中并没有使用这种方法。
可达性分析算法
所有和 GC Roots 直接或间接关联的对象都是有效对象,和 GC Roots 没有关联的对象就是无效对象。
GC Roots 是一个集合
虚拟机栈中的引用对象
方法区中的静态类型引用对象
方法区中常量引用对象(String Table)
JNI 引用对象
虚拟机内部引用(一些常驻对象)
被同步锁(synchronized)持有的对象
对象引用
Strong reference (强引用)
Weak reference(弱引用)
当一个对象仅仅被 weak reference 指向, 而没有任何其他 strong reference 指向的时候, 如果 GC 运行, 那么这个对象就会被回收。
这句话是什么意思呢?
当你把 car 设置为 null 的时候,当编译器发现 car 没有引用的时候,就会触发 GC 进行回收。但就算你不把它设置为 null。如果被编译器发现它没有被使用的话一样会进行回收。
Soft reference(软引用)
软引用则只有在 JVM 内存不足时才有资格被垃圾回收器回收
Phantom Reference(虚引用)
虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收
对象是否必须回收
对于可达性分析算法而言,未到达的对象并非是“非死不可”的,若要宣判一个对象死亡,至少需要经历两次标记阶段。
判定 finalize() 是否有必要执行
JVM 会判断此对象是否有必要执行 finalize() 方法,如果对象没有覆盖 finalize() 方法,或者 finalize() 方法已经被虚拟机调用过,那么视为“没有必要执行”。那么对象基本上就真的被回收了。
如果对象被判定为有必要执行 finalize() 方法,那么对象会被放入一个 F-Queue 队列中,虚拟机会以较低的优先级执行这些 finalize()方法,但不会确保所有的 finalize() 方法都会执行结束。如果 finalize() 方法出现耗时操作,虚拟机就直接停止指向该方法,将对象清除。
对象重生或死亡
如果在执行 finalize() 方法时,将 this 赋给了某一个引用,那么该对象就重生了。如果没有,那么就会被垃圾收集器清除。
任何一个对象的 finalize() 方法只会被系统自动调用一次,如果对象面临下一次回收,它的 finalize() 方法不会被再次执行,想继续在 finalize() 中自救就失效了。
版权声明: 本文为 InfoQ 作者【ue4】的原创文章。
原文链接:【http://xie.infoq.cn/article/a98e1ffcddb9ec5dad440b665】。文章转载请联系作者。
评论