March 3, 2016
·
读书笔记
简述Hotspot的垃圾回收机制
- 本文仅代表Hotspot VM,参考自
- Hotspot使用分代回收策略
- 当一个对象不能被当前运行程序中的任何指针指向时,会被认为是垃圾。更具体的说,Hotspot使用Tracing算法来判断,所有根引用不可达的对象就是垃圾。
- 根引用指:Class对象、当前线程和活动的线程块、monitor(所有执行了wait()/notify()或synchronized锁住的对象)、本地变量、Finalizer(重写了finally的对象会有)、JVM栈的帧(包含局部变量和操作数栈)等。
- (上图指的是JDK8除Parallel和G1算法之外的分代内存描述,JDK8开始移除了永生代的概念)
- Minor collection:收集young generation(=Eden+S1+S2)空间的垃圾
- 一般发生在JVM无法为新对象分配内存时(新对象总是在Eden分配内存),例如Eden满时。
- young generation中“活的”对象越少,minor collection越快
- 一些对象会在多次minor collection后,移动到tenured generation
- Major collection:当tenured generation充满时触发,清理整个heap的内存(也称Full GC)
- 不同的垃圾回收算法
- HotSpot VM of JDK8提供了三种(也可以说四种)回收算法
- Serial collector:单线程回收,适用于单核CPU或小内存应用。
- JVM Client模式默认垃圾回收器
- 对应-XX:+UseSerialGC
- Parallel collector(也叫Throughput collector):允许Minor GC并行。其中包含的Parallel compaction允许Major GC并行。
- JVM Server模式默认垃圾回收器
- 对应-XX:+UseParallelGC
- Parallel compaction默认开启,使用 -XX:-UseParallelOldGC可以关闭
- Mostly concurrent collectors:旨在最大化降低GC暂停时间(尤其是老生代),包括两种
- Concurrent Mark Sweep (CMS) Collector
- 对应-XX:+UseConcMarkSweepGC
- Garbage-First(G1) Garbage Collector
- 如何选择垃圾回收算法?
- 除非应用有严格的暂停时间限制,建议首先让VM自己选择,如果需要的话调整heap大小。如果此时仍不能满足性能要求,尝试采用下面的方式:
- 如果应用内存小于100M,使用Serial collector
- 如果应用在一个处理器核心上运行,并且没有暂停时间限制,建议让VM自己选择或者使用Serial collector
- 如果注重峰值时的程序性能,并且没有暂停时间限制(可以忍受1s或以上的暂停),建议让VM自己选择或者使用Parallel collector
- 如果注重程序响应时间(即不能忍受暂停时间),使用CMS或G1
- 内存的分配
- 为了提高速度,使用了指针碰撞(bump-the-pointer)技术:跟踪上一次分配内存的对象的末尾地址,当新对象分配内存时仅需判断剩余空间是否足够,足够则按顺序继续分配,否则执行Minor GC。
- 为了提高多线程分配速度,使用了TLABs(Thread-Local Allocation Buffers,线程本地分配缓冲)技术:每个线程在Eden区都有一小块私有空间,分配内存时可以实现无锁分配。(除非TLABs区满)
- Serial collector
- 新生代
- 当Eden满时触发GC,此时会把Eden中活着的对象和Survivor1区(每个时刻总会有一个Survivor区为空,这里假设此时Survivor1区在用,Survivor2区为空)活着的对象迁移到Survivor2(如果放不下,多余的对象会放入Tenured区)。下一次触发GC时就会把Eden和Survivor2区活着的对象迁移到Survivor1。以此类推。
- 老生代
- 使用“标记-清除-整理”算法:
- 1. 标记老年代存活的对象
- 2. 清理死亡的对象
- 3. 整理内存空间,使存活的对象连续存放
- Parallel collector
- 新生代
- 算法与上面相同,不过允许并行(具体细节待补充),可以充分利用多核优势
- 老生代(Parallel compaction)
- 执行三个阶段:标记-汇总-压缩(mark – summary – compaction)
- 在标记阶段,运行中代码的直接引用对象(根对象)会根据gc线程数拆分,使标记阶段并行执行。
- 在汇总阶段,由于老生代中存活的对象往往集中在左侧,左侧的一些小碎片是不值得去压缩的。因此首先会找到值得压缩的临界点:其左侧的对象维持不动,右侧才会移除垃圾对象。
- 注意这个阶段目前(JDK5)是串行执行的,因为此阶段相比其他两个阶段耗时较短
- 在压缩阶段,各线程并行的拷贝压缩,形成左密右空的内存布局。
- 细节:并行GC算法导致Major GC(Full GC)的情况
- 1. 要(从新生代)晋升的对象大小大于老生代剩余空间时
- 2. 老生代的剩余空间小于平均晋升对象大小时
- CMS collector
- 新生代
- 老生代
- 大部分老生代的CMS GC是和程序代码并发(concurrent)执行的
- 主要步骤
- 1. initial-mark,暂停JVM,找到所有的root根的引用,标记在一个bitmap中(单线程执行)
- 2. concurrent-mark,恢复程序运行,并发的标记所有根引用的可达对象(还有处理young-old之间的引用等细节)
- 3. remark,由于上一步程序又执行了一段时间,需要再次暂停JVM,并更新这段时间修改过的对象状态(实现关键词:incremental update write barrier)(多线程执行)
- 4. concurrent-sweep,恢复程序运行,直接释放掉垃圾对象
- 注意,CMS算法释放垃圾对象之后不做内存压缩,因此需要建立一个可用内存列表用来分配内存。这也使得分配内存(即从新生代晋升)的性能下降。
- CMS并不是当老生代变满时进行GC,而是根据统计数据(历史GC耗时,老生代何时充满)自己决定。同时如果老生代的使用率超过initiating occupancy(默认值68%)时,也会触发GC。
- CMS还提供了增量模式(Incremental Mode)的选项,开启后在CMS的并发阶段(上图中的Concurrent Mark和Concurrent Sweep)会周期性的暂停,目的是减少对程序线程的干扰。通常用于单核或少核CPU。
- Garbage-First(G1) Collector
- 由于Hotspot缺乏相关文档,此处对G1的简单阐述针对算法本身
- 内存空间分配
- 整个heap被分成了若干个等大连续(指虚拟地址)的内存块(region)。这些区域被映射为逻辑上的 Eden, Survivor, tenured(old) , Humongous regions(用于存储大对象)和一些暂未使用的空间。
- 收集步骤(来自RednaxelaFX,见 http://hllvm.group.iteye.com/group/topic/44381)
- 1. 全局并发标记(global concurrent marking)
- 初始标记(initial marking):暂停阶段。找到所有的root根的引用,压入扫描栈中等到后续扫描。
- 并发标记(concurrent marking):并发阶段。递归扫描扫描栈的引用,标记所有可达对象。
- 最终标记(final marking/remarking):暂停阶段。处理上个阶段的增量更新,实现关键词SATB write barrier(Snapshot-At-The-Beginning),此处的算法优于CMS。
- 清理(cleanup):暂停阶段。在marking bitmap里统计每个region被标记为活的对象有多少。
- 2. 拷贝存活对象(evacuation)
- 全暂停阶段。自由选择任意多个region来独立收集构成收集集合(collection set,简称CSet)。选定CSet后,采用并行copying(类似Parallel Young GC)算法把CSet里每个region里的活对象拷贝到新的region里。
- CSet的选定:
- Young GC:选定所有young gen里的region。通过控制young gen的region个数来控制young GC的开销。
- Mixed GC:选定所有young gen里的region,外加根据global concurrent marking统计得出收集收益高的若干old gen region。在用户指定的开销目标范围内尽可能选择收益高的old gen region。
- 分代式G1的正常工作流程就是在young GC与mixed GC之间视情况切换。如果old gen填满无法继续进行mixed GC,会切换到G1之外的serial old GC来收集整个heap。
- 其降低延迟的关键,就是不会evacuate所有有活对象的region(虽然标记使全局的),通过只选择收益高的少量region来evacuate,这种暂停的开销就可以(在一定范围内)可控
- 补充