jvm 05-JVM垃圾收集策略
Posted liangjingfu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jvm 05-JVM垃圾收集策略相关的知识,希望对你有一定的参考价值。
GC策略
- 新生代GC策略:
- 串行GC:Serial Copying
- 并行回收GC:Parallel Scavenge
- 并行GC:ParNew
- 老年代GC策略:
- 串行GC:Serial MSC
- 并行GC:Parallel MSC
- 并发GC:CMC
新生代GC策略
新生代--串行GC(Serial Copying)
- 算法:复制清理算法
- 操作步骤:
- 扫描新生代中所有存活的对象
- 使用Minor GC进行垃圾回收,同时将存活对象保存到S0 or S1区
- 在上一次Minor GC的基础上进行S0和S1区的角色交换
- 经历许多次Minor GC依然存活的对象晋升到老年代
新生代--并行回收GC(Parallel Scavenge)
- 算法:复制清理算法
- 操作步骤:
- 在扫描和复制时均采用多线程方式处理
- 在新生代使用并行GC处理的时候会产生有一个STW(stop The World pause)的暂停,该暂停主要是进行对象回收,期间其他的线程将被暂时性挂起。
- 并行回收GC为空间较大的新生代回收提供许多优化
- 在多CPU的机器上,其GC耗时会比串行方式短
- 该方式适合多CPU、对暂停时间要求较短的应用
新生代--并行GC(ParNew)
- 算法:复制清理算法
- 操作步骤:
- 并行GC必须结合老年代CMS GC一起使用。
- 因为在新生代如果发生了Minor GC时,老年代也需要使用CMS GC同时处理
- CMS(Concurrent Mark-Sweep)是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器,主要针对要求服务器响应速度快的应用
老年代GC策略
老年代--串行GC(Serial MSC)
- 算法:标记-清除-压缩(Mark-Sweep-Compact)
- 特点:串行执行的过程中为单线程,需要暂停应用并耗时较长
- 操作步骤:
- 扫描老年代中还存活的对象,并且对这些对象进行标记
- 遍历整个老年代内存空间,回收所有未标记的对象内存
- 将所有存活对象都集中在一端,而后将所有回收对象的内存空间变为一块连续的内存空间
老年代--并行GC(Parallel )
- 算法:标记-压缩(Mark-Compact)
- 特点:
- 多线程同时进行垃圾回收可以缩短应用的暂停时间
- 但是由于老年代的空间一般较大,在扫描和标记存活对象上还是需要花费较长的时间
- 操作步骤:
- 将老年代内存空间按照线程个数划分为若干个子区域
- 多线程并行对各自子区域内的存活对象进行标记
- 多线程并行清除所有未标记的对象
- 多线程并行将多个存活对象整理在一起,并将所有回收的对象空间整合为一体
老年代--并发GC(Concurrent Makr-Sweep GC、CMS GC )
- 算法:标记-清除(Mark-Sweep)
- 特点:
- 只有在第一次和重新标记阶段才会暂停整个应用,这样对应用程序所带来的影响非常小。
- 并发标记与回收线程会与应用线程争抢CPU资源,并且容易产生内存碎片。
- 操作步骤:
- 初始标记(STW Initial Mark):虚拟机暂停正在执行的任务(STW),由根对象扫描出所有的关联对象并做出标记。此过程只会导致短暂的JVM暂停。
- 并发标记(Concurrent Marking):恢复所有暂停的线程对象,并且对之前标记过的对象进行扫描,取得所有跟标记对象有关联的对象。
- 并发预清理(Concurrent Precleaning):查找所有在并发标记阶段新进入老年代的对象(可能是从新生代晋升到老年代或被分配到老年代),通过重新扫描,减少下一阶段的工作。
- 重新标记(STW Remark):此阶段会暂停虚拟机,对在并发标记阶段被改变引用或新创建的对象进行标记
- 并发清理(Concurrent Sweeping):恢复所有暂停的应用线程,对所有未标记的垃圾对象进行清理,并且尽量将已回收对象的空间重新拼凑为一个整体。在此阶段收集器线程和应用程序线程并发执行。
- 并发重置(Concurrent Reset):重置CMS收集器的数据结构,等待下一次垃圾回收。
GC参数
GC策略调整参数
- -XX:+UseSerialGC -- Serial Copying + Serial MSC
- -XX:+UseParallelGC -- Parallel Scavenge + Parallel Mark Sweep
- -XX:+UseConcMarkSweepGC -- ParNew + Concurrent Mark-Sweep GC
- -XX:+UseParNewGC -- ParNew + Serial MSC
- -XX:+UseParallelOldGC -- Parallel Scavenge + Parallel Mark Sweep
收集参数设置
- -XX:ParallelGCThreads=n:设置并行收集器操作使用的CPU数量
- -XX:MaxGCPauseMillis=n:设置并行收集器的最大暂停时间,单位为毫秒
- -XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比
- -XX:+CMSIncrementalMode:设置增量模式,适合单CPU模式。需要使用-XX:+UseConcMarkSweepGC策略
测试代码:
package cn.liang.jvm; public class gctest2 { public static void main(String[] args) { String string = "liang"; while (true) { string += string + string; string.intern(); } } }
默认的GC策略:Parallel Scavenge + Parallel MSC
-Xmx10m -Xms10m -XX:+PrintGCDetails
输出结果: [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] [ParOldGen: 4082K->4070K(7168K)] 4082K->4070K(8704K), [Metaspace: 2649K->2649K(1056768K)], 0.0047164 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
使用串行GC策略:Serial Copying + Serial MSC
-Xmx10m -Xms10m -XX:+UseSerialGC -XX:+PrintGCDetails
输出结果: [GC (Allocation Failure) [DefNew: 2581K->2581K(3072K), 0.0000758 secs] [Tenured: 5989K->4084K(6848K), 0.0034008 secs] 8570K->4084K(9920K), [Metaspace: 2649K->2649K(1056768K)], 0.0035313 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
使用并行GC策略:Parallel Scavenge + Parallel Mark Sweep
-Xmx10m -Xms10m -XX:+UseParallelGC -XX:+PrintGCDetails
输出结果: [Full GC (Allocation Failure) [PSYoungGen: 224K->0K(2560K)] [ParOldGen: 5347K->2816K(7168K)] 5571K->2816K(9728K), [Metaspace: 2648K->2648K(1056768K)], 0.0039458 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
使用CMS回收:ParNew + Concurrent Mark-Sweep GC
- 这对整个程序的暂停时间会非常短暂,适合于响应速度快的程序
-Xmx10m -Xms10m -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails
输出结果: [GC (Allocation Failure) [ParNew: 2584K->2584K(3072K), 0.0000325 secs] [CMS (concurrent mode failure): 4941K->4087K(6848K), 0.0030993 secs] 7526K->4087K(9920K), [Metaspace: 2649K->2649K(1056768K)], 0.0032009 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
总结:
- 如果程序没有对运行速度要求的话,建议使用默认的GC策略
- 所有的GC策略都有一个问题:都需要扫描全部子内存
以上是关于jvm 05-JVM垃圾收集策略的主要内容,如果未能解决你的问题,请参考以下文章