垃圾收集器
书到用时方恨少,事非经过不知难!本文参考《深入理解JVM》周至明著。由于写作水平和写作时间有限,本中存在不妥之处,还请大家多多留言。
判定对象死亡
-
引用计数算法
思想:创建对象时并给其添加一个引用计数器,当某一地方引用它时,计数器值+1,当引用失效时,计数器值-1;任何计数器为0的对象就不可能再被使用。
缺点:难解决对象之间相互循环引用的问题。
-
可达性分析算法(主流商用语言:Java,C#等)
思想:通过一系列 “GC Roots” 对象作为起始点,从这些节点向下搜索,搜索经过的路径称为引用链(Reference Chain),当某一个对象到 GC Roots 没有任何引用链连接时,则证明对象不可能再被引用。
- Java语言中,GC Roots的对象
-
虚拟机栈中引用对象。
-
方法区中类静态属性引用的对象。
-
方法区中常量引用的对象。
-
本地方法栈中JNI(一般指Native方法)引用的对象
-
垃圾收集算法
-
标记-清除算法(Mark-Sweep)
过程:分为2个阶段,标记和清除。首先标记处所有需要清理的对象,标记完成之后,统一回收被标记的所有对象。
不足:
- 效率:标记和清除效率都不高。
- 空间问题:标记清除完之后,可能产生大量的不连续内存碎片,会导致分配内存较大对象,无法找到连续空间内存,从而提前触发一次垃圾收集。
工作如下图所示
-
复制算法
基本思想:将可用内存按容量分为大小相等2块(用A、B代指),每次先使用A,当A使用完了,就A现存活对象复制到B中,然后一次将A的内存空间清空。
优点:不考虑内存碎片等复杂情况。
缺点:将内存缩小为原来一半。
工作如下图所示:
-
标记-整理算法
基本思想:先标记完所有可回收对象,然后让存活对象向一端移动,最后清理掉剩下的内存。
“标记-整理算法”如下图所示:
-
分代收集算法
当前商业虚拟机垃圾收集都采用“分代收集”(Generational Collection)算法,根据对象存活周期不同划分为几块;一般将其分为新生代和老年代。
新生代特点:每次垃圾收集时都有大批对象死去,只要少量存活。(采用复制算法)
老年代特点:对象存活率高、没有额外空间对它进行分配担保,就采用“标记-清理”或“标记-整理”算法来进行回收。
分配担保机制:某一空间内存(譬如:新生代)不够时,需要依赖另一空间(譬如:老年代)进行担保。如生活中去银行借款,需要一个担保人一样。
HotSpot的算法实现
在安全点/安全域中使用OopMap结构完成根节点枚举。
-
枚举根节点
在HotSpot 实现根节点枚举时,遇到2个问题:1、数据一致性:在判定对象过程中必须保证对象引用关系不变。2、消耗时间多:逐个检查引用。
解决办法:通过使用一组称为OopMap的数据结构,在类加载、编译过程中确定偏移量及对应的数据类型,在特定位置记录栈和寄存器中哪些位置是引用。
-
安全点
一般在方法调用、循环跳转、异常跳转等“长时间执行”的地方,才会产生安全点(Safepoint)。即指上面提到的“特定位置”。
-
安全域
在一段代码中,引用关系不会发生变化。
垃圾收集器
以垃圾收集线程与用户线程为例:
并行(Parallel):多条垃圾收集线程并行工作。
并发(Concurrent):用户线程与垃圾收集线程同时进行。
下图展示7种收集器,2个收集器之间存在连线,表名可搭配使用。
-
Serial收集器
特点:单线程的收集器,在它进行垃圾收集时,必须暂停其他所有工作线程,直到它收集结束。
适用场景:JVM 运行在Client模式下的默认新生代收集器。
- ParNew收集器
特点:使用多条线程进行垃圾收集。
适用场景:Service模式下的JVM中首选的新生代收集器。
- Paraller Scavege收集器
特点:新生代收集器;一个可控制的吞吐量。即吞吐量=运行用户代码时间/(垃圾收集时间+运行用户代码时间)。
GC自适应的调节策略(GC Ergonomics):虚拟机根据当前系统运行情况收集性能监控信息,动态调整参数。
- Serial Old收集器
使用“标记-整理”算法,单线程收集器。
- Paraller Old收集器
使用多线程和“标记-整理”算法
- CMS 收集器(Concurrent Mark Sweep)
目标:获取最短回收停顿时间。基于“标记-清除”算法。
- 过程:
- 初始标记:标记GC Roots关联到的对象,速度块。
- 并发标记(耗时):进行GC Roots Tracing的过程。
- 重新标记:修正用户程序运行过程中,导致标记产生变动的对象的标记记录。
- 并发清除(耗时)
- 缺点:
- CMS无法处理浮动垃圾(Floating Garbage):GC下一次清理浮动垃圾。在并发清理阶段,用户线程运行时产生的新垃圾,即为浮动垃圾。
- 采用“标记-清除”算法,产生大量的控件碎片。解决:为其提供内存碎片合并的参数。
- G1收集器(Garbage-First)
在JDK1.7之后采用此收集器,用于服务端应用的垃圾收集器。
特点:
- 并发与并行
- 分代收集
- 空间整合
- 可预测停顿。
- 过程
- 初始化标记
- 并发标记:耗时长,与用户线程并发执行。
- 最终标记
- 筛选回收