深入理解JVM - 垃圾收集
1. 概述
垃圾收集器 Garbage Collection。
垃圾收集器需要完成的三件事
- 哪些内存需要回收
- 什么时候回收
- 如何回收
只有Java堆和方法区需要考虑内存回收,程序计数器、虚拟机栈、本地方法栈的内存分配和回收都具有确定性。
2. 如何判断对象已死
1. 引用计数法 Reference Counting
在对象中添加一个引用计数器,每当被其他对象引用,计数器+1,引用失效则计数器-1,当计数器值为0时表名该对象不可能再被使用。
原理简单、判定效率高,难以解决循环引用问题。
2. 可达性分析算法 Reachability Analysis
通过一系列称之为 GC Roots
的根对象作为起始集,从这些根对象开始,根据引用链关系向下搜索,搜索过程走过的路径称之为引用链 Reference Chain
,如果某个对象到GC Roots
间没有引用链相连,也即从GC Roots
到这个对象不可达,则证明该对象不可再被使用。
GC Roots
- 虚拟机栈(栈帧中的局部变量表)中引用的对象,如参数、局部变量、临时变量等。
- 本地方法栈中Native方法引用的对象
- 方法区中类静态属性引用的对象,如Java类的引用型静态字段
- 方法区中常量引用的对象,如字符串常量池中的引用
- VM内部引用,如基本数据类型对应的Class对象,常驻异常对象(NPE、OOM),系统类加载器等
- 同步锁/监视器锁 synchronized 所持有的对象
- 反应 JVM 内部情况 JMXBean、JVMTI 中注册的回调、本地代码缓存等。
以上是固定 GC Roots
,根据用户选择的垃圾收集器及当前回收的内存区域不同,还可以有其他临时性对象加入。如分代收集和局部回收(Partial GC),针对局部剧区域的回收,还需要考虑该区域内对象被其他区域对象引用。当然为了避免 GC Roots
的膨胀,都进行了各种优化处理。
3. 各种引用
强软弱虚四种引用。
强引用 Stringly Reference
:最传统的引用,只要还可达,就不会被回收。
软引用 Soft Reference
:OOM前对这些引用进行回收,若内存还不足才会OOM。SoftReference 类
弱引用 Weak Reference
:只能生存到下一次 GC。WeakReference 类。
虚引用 Phantom Reference
:虚引用的存在不影响对象本身的生存时间,只是在对象被回收时得到通知。PhantomReference 类。
4. 标记过程
要宣告一个对象 不可达,至少需要经历两次标记。
第一次判断不可达,判断 finalize 方法是否已重写或是否已被 VM 调用过。若没有,则放入 F-Queue 队列,由 VM 自动建立的,低优先级的 Finalizer 线程执行 finalize 方法。
第二次对F-Queue对象判断是否可达,若不可达则此对象宣告死亡。
finalize 释放资源的问题:执行时间不确定,若某对象 fianlize 执行缓慢甚至死循环,可能导致内存回收子系统的崩溃。
当对象不可达时,可在 finalize 中拯救自己一次。
5. 方法区回收
主要两部分内容:废弃的常量和不再使用的内存。
类不再使用
- 实例全部已被回收
- 加载该类的类加载器已被回收
- 该类的 Class 对象没有被引用
-Xnoclassgc、-verbose:class、-XX:+TraceClassLoading、-XX:+TraceClassUnLoading