JVM系统优化实践(7):垃圾回收器与垃圾回收算法

您好,我是湘王,这是我的CSDN博客,欢迎您来,欢迎您再来~


上回说到了年轻代、老年代与数据计算的一个案例。接下来就先讲一讲年轻代和老年代的两个垃圾回收器:ParNew和CMS。

和Serial垃圾回收器一样,ParNew也是对年轻代进行垃圾回收,但Serial垃圾回收器是单线程的,而ParNew是多线程的。

如果要指定使用ParNew垃圾回收器,只需在JVM中开启-XX:+UseParNewGC参数就行了。

JVM系统优化实践(7):垃圾回收器与垃圾回收算法_第1张图片

ParNew的默认线程数量和CPU核数一致,如果要调节ParNew线程数量,使用-XX:ParallelGCThreads参数即可,一般不用更改。启动系统时可以分别指定客户端模式与服务器模式,只要在参数后分别跟上-client和-server就行了。客户端模式与服务器模式的区别是:

1、如果部署在类Unix机器上,就用服务器模式;如果部署在Windows上,就是客户端模式;

2、服务器模式通常是多核的大型系统后端;客户端模式通常是给applet这类的客户端程序使用,机器也多为单核。

而老年代的垃圾回收器CMS使用的是标记清理算法:标记出哪些是垃圾对象,然后就把它们清理掉。但这可能会造成内存碎片过多,导致空间严重浪费。因此,CMS采取GC线程 + 工作线程同时执行的方式。

JVM系统优化实践(7):垃圾回收器与垃圾回收算法_第2张图片

CMS的一次典型垃圾回收过程如下:

1、初始标记阶段。初始标记意味着系统要进入Stop the World状态,仅仅标记出所有被GC Roots直接引用的对象(方法的局部变量和静态变量),并不会执行清理。

JVM系统优化实践(7):垃圾回收器与垃圾回收算法_第3张图片

2、并发标记阶段。系统程序恢复运行,GC线程则会尽可能对全部老年代里已有对象进行GC Roots追踪(即弄清全部老年代里的对象是否被引用了)。

JVM系统优化实践(7):垃圾回收器与垃圾回收算法_第4张图片

3、重新标记阶段。系统程序再次被禁止运行,对在上一阶段程序运行时状态发生变更的对象进行标记。

JVM系统优化实践(7):垃圾回收器与垃圾回收算法_第5张图片

4、最后是并发清理阶段。系统程序再次恢复运行,清理掉之前标记为垃圾的对象即可。

JVM系统优化实践(7):垃圾回收器与垃圾回收算法_第6张图片

因此这就可以解释为什么老年代会比年轻代速度慢很多了。主要就是CMS会导致资源紧张。系统线程和GC线程同时工作,会导致有限的CPU资源被占用,尤其是进行GC Roots时。

CMS默认启动的GC线程数量 = (CPU核数 + 3) / 4。

在第四阶段(即并发清理阶段),会产生浮动垃圾,即GC本身产生的新垃圾。

JVM系统优化实践(7):垃圾回收器与垃圾回收算法_第7张图片

浮动垃圾要等到下一次Minor GC时才会被回收。所以JVM会预留一些空间,保证在CMS期间让存活对象能进入老年代。前面也说过CMS的触发时机就是当老年代内存占用达到一定比例时启动。-XX:CMSInitiatingOccupancyFraction参数就是用来设置这个阈值,超过设定值则开启CMS(JDK1.8已不建议使用)。JDK8的默认比例值是92%,具体原因可以参考这个官方链接:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html

另外,CMS执行时会遇到一个Concurrent Mode Failure问题:也就是当要进入到老年代的存活对象大于可用内存空间时,意味着CMS执行失败。此时会自动用Serial Old代替CMS——强行Stop the World,垃圾回收完成后再恢复系统线程。

执行GC时产生的内存碎片由这两个JVM参数决定:

1、-XX:UseCMSCompactAtFullCollection参数会默认打开,避免CMS产生的内存碎片问题,意思是Full GC之后再次进行Stop the World,清理碎片(JDK1.8已不建议使用);

2、-XX:CMSFullGCsBeforeCompaction参数表示执行多少次Full GC之后再进行碎片清理(默认0,表示每次都清理,JDK1.8已不建议使用)。


感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~

你可能感兴趣的:(技术,Java,JVM,垃圾回收算法,ParNew,CMS)