Java两种垃圾回收器G1与CMS
JVM 体系架构
JVM主要组成组件:类加载器(class loader),运行时数据区(runtime data areas),执行引擎(execution engine)
堆内存是存放对象的内存空间,根据系统场景选用合适的垃圾回收器。
性能基础
程序运行主要关注,吞吐量和相应速度两个指标。
- 常用的b/s架构多关注与相应速度,较大的系统停顿时间不可接受,需求在非常短的时间周期内对用户请求做快速响应。
- 吞吐量关注cpu用于工作时间/cpu全部时间,跟关心尽快完成工作任务,而不考虑速度。较大的停顿时间可以接受。
CMS 垃圾回收器
CMS垃圾回收器(CMS, Concurrent Mark Sweep)并行标记清理垃圾回收器,是一种多并发低停顿的垃圾收集器,回收老年代内存。垃圾收集线程与工作线程并发执行,不对内存复制或也不压缩存活的对象。如果产生内存碎片问题,会通过FULL GC 方式进行垃圾回收。
CMS堆内存结构
对内存划分为三个空间,年轻代(Young generation)分为 1个新生代空间(Eden)和2个存活区(survivor spaces)、老年代(Old generation)。
年轻代垃圾回收
同一时间内新生代内存只会使用Eden区和一个Survivors区,垃圾回收时会将Eden区和survivor区中的存活对象被拷贝到另一个空的survivor 区,当新生代对象存活时间达到阀值,对象会被提升到老年代。
新生代来及回收之后,内存如下:
CMS 老年代回收
当老年代内存占用率达到一定比率时,CMS垃圾回收器就开始工作。
老年代垃圾回收会经历4个过程:
- 初始标记 :标记GC Roots能直接关联到的对象,需要在safepoint位置暂停所有执行线程。
- 并发标记 :进行GC Roots Tracing,遍历完从root可达的所有对象。该阶段与工作线程并发执行。
- 重新标记 :修正并发标记期间因用户程序继续运作而导致标记产生表动的那一部分对象的标记记录。需要在safepoint位置暂停所有执行线程。
- 并发清理 :内存回收阶段,将死亡的内存对象占用的空间增加到一个空闲列表(free list),供以后的分配使用。
-
重置 :清理数据结构,为下一个并发收集做准备。
老年代并发标记内存如下:
内存回收整理后如下:整理之后释放了很多死亡的内存,但并没有进行压缩和整理。
G1 垃圾回收器
G1垃圾回收器是主要针对多处理器以及大内存的机器,以极高的概率满足预测GC停顿时间要求的同时,还具备高吞吐量性能特征。是基于标记整理的垃圾回收器。
G1堆管理方式
堆内存被划分为多个大小相等的逻辑heap 区,其中一部分区域被当成老一代收集器相同的角色(eden, survivor, old), 但每个角色的区域个数都不是固定的。
每个堆Region 的大小在JVM启动时就确定了,JVM通常生成2000个Region,每个Region 大小在1M-32M之间。这些Region会被逻辑映射成Eden, Survivor, 和 old generation(老年代)空间。
G1新生代垃圾回收
新生代中存活的对象被转移到一个或者多个Survivor区,如果达到存活阀值则这部分对象就会被迁移到old 老年区。在回收过程中会有一次STW暂停,会计算Eden和Survivor大小给下一次GC使用。
新生代回收之前:
新生代回收结束后:新生代被转移到Survivor区或者old区,并行垃圾收集。
G1老年代垃圾回收
- 初始标记(initial mark,STW)
在此阶段,G1 GC 对根进行标记。该阶段与常规的 (STW) 年轻代垃圾回收密切相关。 - 根区域扫描(root region scan)
G1 GC 在初始标记的存活区扫描对老年代的引用,并标记被引用的对象。该阶段与应用程序(非 STW)同时运行,并且只有完成该阶段后,才能开始下一次 STW 年轻代垃圾回收。 - 并发标记(Concurrent Marking)
G1 GC 在整个堆中查找可访问的(存活的)对象。该阶段与应用程序同时运行,可以被 STW 年轻代垃圾回收中断 - 最终标记(Remark,STW)
该阶段是 STW 回收,帮助完成标记周期。G1 GC 清空 SATB 缓冲区,跟踪未被访问的存活对象,并执行引用处理。 - 清除垃圾(Cleanup,STW)
在这个最后阶段,G1 GC 执行统计和 RSet 净化的 STW 操作。在统计期间,G1 GC 会识别完全空闲的区域和可供进行混合垃圾回收的区域。清理阶段在将空白区域重置并返回到空闲列表时为部分并发。