0002-JVM之cms垃圾回收器

CMS 处理过程有七个步骤: 
1. 初始标记(CMS-initial-mark) ,会导致swt; 
2. 并发标记(CMS-concurrent-mark),与用户线程同时运行; 
3. 预清理(CMS-concurrent-preclean),与用户线程同时运行; 
4. 可被终止的预清理(CMS-concurrent-abortable-preclean) 与用户线程同时运行; 
5. 重新标记(CMS-remark) ,会导致swt; 
6. 并发清除(CMS-concurrent-sweep),与用户线程同时运行; 
7. 并发重置状态等待下次CMS的触发(CMS-concurrent-reset),与用户线程同时运行; 
 

初始标记

这是CMS中两次stop-the-world事件中的一次。这一步的作用是标记存活的对象,有两部分: 
1. 标记老年代中所有的GC Roots对象,如下图节点1; 
2. 标记年轻代中活着的对象引用到的老年代的对象(指的是年轻带中还存活的引用类型对象,引用指向老年代中的对象)

在Java语言里,可作为GC Roots对象的包括如下几种: 
1. 虚拟机栈(栈桢中的本地变量表)中的引用的对象 ; 
2. 方法区中的类静态属性引用的对象 ; 
3. 方法区中的常量引用的对象 ; 
4. 本地方法栈中JNI的引用的对象; 

何为gc root
通过 gc root对象作为起点,向下进行搜索,如果这个对象可达,则该对象不可回收,若该对象不可达,则可以回收.

该gc root对象
    class
    thread:活着的线程
    Stack Local - Java方法的local变量或参数

何为stw:

 执行垃圾收集算法时,Java应用程序的其他所有除了垃圾收集收集器线程之外的线程都被挂起,此时,系统只能允许GC线程进行运行,其他线程则会全部暂停,等待GC线程执行完毕后才能再次运行。

并发标记

该步骤主要做两件事:

1,从“初始标记”阶段标记的对象开始找出所有存活的对象;

2,因为是并发运行的,工作线程和gc线程是同时运行的,并发标记阶段会将该阶段发生GC ROOT 不可达对象标记为dirty.该阶段只负责标记,不负责处理.

重新标记

这个阶段会导致第二次stop the word,该阶段的任务是完成标记整个年老代的所有的存活对象

重新标记是扫描整个堆,包括年轻带和老年代,为什么要扫描年轻代,因为年轻代的对象有可能会引用老年代的对象,在进行重新标记之前,会先进行一次yong gc,remark阶段要花很多时间处理这些改变,会导致很长stop the word,所以通常CMS尽量运行Final Remark阶段在年轻代是足够干净的时候

并发清理

通过以上5个阶段的标记,老年代所有存活的对象已经被标记并且现在要通过Garbage Collector采用清扫的方式回收那些不能用的对象了。 
这个阶段主要是清除那些没有标记的对象并且回收空间;

由于CMS并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在当次收集中处理掉它们,只好留待下一次GC时再清理掉。这一部分垃圾就称为“浮动垃圾”。

CMS需要注意的问题

cms减少remark时间

一般CMS的GC耗时 80%都在remark阶段,如果发现remark阶段停顿时间很长

-XX:+CMSScavengeBeforeRemark

该参数主要是进行老年代回收之前先进行一次yong gc.

 

一些常见cms配置参数

-XX:+PrintCommandLineFlags                  [0]

     -XX:+UseConcMarkSweepGC                     [1]  

     -XX:+UseCMSInitiatingOccupancyOnly          [2]

     -XX:CMSInitiatingOccupancyFraction=80       [3]

     -XX:+CMSClassUnloadingEnabled               [4]

     -XX:+UseParNewGC                            [5]

     -XX:+CMSParallelRemarkEnabled               [6]

     -XX:+CMSScavengeBeforeRemark                [7]

     -XX:+UseCMSCompactAtFullCollection          [8]

     -XX:CMSFullGCsBeforeCompaction=0            [9]

     -XX:+CMSConcurrentMTEnabled                 [10]

     -XX:ConcGCThreads=4                         [11]

     -XX:+ExplicitGCInvokesConcurrent            [12]

     -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses    [13]

     -XX:+CMSParallelInitialMarkEnabled          [14]

 

     -XX:+PrintGCDetails                         [15]

     -XX:+PrintGCCause                           [16]

     -XX:+PrintGCTimeStamps                      [17]

     -XX:+PrintGCDateStamps                      [18]

     -Xloggc:../logs/gc.log                      [19]

     -XX:+HeapDumpOnOutOfMemoryError             [20]

     -XX:HeapDumpPath=../dump                    [21]

 

 

先来介绍下下面几个参数的作用:

0. [0]打印出启动参数行

1. [1]参数指定使用CMS垃圾回收器;

2. [2]、[3]参数指定CMS垃圾回收器在老年代达到80%的时候开始工作,如果不指定那么默认的值为92%;

3. [4]开启永久带(jdk1.8以下版本)或元数据区(jdk1.8及其以上版本)收集,如果没有设置这个标志,一旦永久代或元数据区耗尽空间也会尝试进行垃圾回收,但是收集不会是并行的,而再一次进行Full GC;

4. [5] 使用cms时默认这个参数就是打开的,不需要配置,cms只回收老年代,年轻带只能配合Parallel New或Serial回收器;

5. [6] 减少Remark阶段暂停的时间,启用并行Remark,如果Remark阶段暂停时间长,可以启用这个参数

6. [7] 如果Remark阶段暂停时间太长,可以启用这个参数,在Remark执行之前,先做一次ygc。因为这个阶段,年轻带也是cms的gcroot,cms会扫描年轻带指向老年代对象的引用,如果年轻带有大量引用需要被扫描,会让Remark阶段耗时增加;

7. [8]、[9]两个参数是针对cms垃圾回收器碎片做优化的,CMS是不会移动内存的, 运行时间长了,会产生很多内存碎片, 导致没有一段连续区域可以存放大对象,出现”promotion failed”、”concurrent mode failure”, 导致fullgc,启用UseCMSCompactAtFullCollection 在FULL GC的时候, 对年老代的内存进行压缩。-XX:CMSFullGCsBeforeCompaction=0 则是代表多少次FGC后对老年代做压缩操作,默认值为0,代表每次都压缩, 把对象移动到内存的最左边,可能会影响性能,但是可以消除碎片;

106.641: [GC 106.641: [ParNew (promotion failed): 14784K->14784K(14784K), 0.0370328 secs]106.678: [CMS106.715: [CMS-concurrent-mark: 0.065/0.103 secs] [Times: user=0.17 sys=0.00, real=0.11 secs]

(concurrent mode failure): 41568K->27787K(49152K), 0.2128504 secs] 52402K->27787K(63936K), [CMS Perm : 2086K->2086K(12288K)], 0.2499776 secs] [Times: user=0.28 sys=0.00, real=0.25 secs]

8. [11]定义并发CMS过程运行时的线程数。比如value=4意味着CMS周期的所有阶段都以4个线程来执行。尽管更多的线程会加快并发CMS过程,但其也会带来额外的同步开销。因此,对于特定的应用程序,应该通过测试来判断增加CMS线程数是否真的能够带来性能的提升。如果未设置这个参数,JVM会根据并行收集器中的-XX:ParallelGCThreads参数的值来计算出默认的并行CMS线程数:

ParallelGCThreads = (ncpus <=8 ? ncpus : 8+(ncpus-8)*5/8) ,ncpus为cpu个数,

ConcGCThreads =(ParallelGCThreads + 3)/4

这个参数一般不要自己设置,使用默认就好,除非发现默认的参数有调整的必要;

9. [12]、[13]开启foreground CMS GC,CMS gc 有两种模式,background和foreground,正常的cms gc使用background模式,就是我们平时说的cms gc;当并发收集失败或者调用了System.gc()的时候,就会导致一次full gc,这个fullgc是不是cms回收,而是Serial单线程回收器,加入了参数[12]后,执行full gc的时候,就变成了CMS foreground gc,它是并行full gc,只会执行cms中stop the world阶段的操作,效率比单线程Serial full GC要高;需要注意的是它只会回收old,因为cms收集器是老年代收集器;而正常的Serial收集是包含整个堆的,加入了参数[13],代表永久带也会被cms收集;

10. [14] 开启初始标记过程中的并行化,进一步提升初始化标记效率;

11. [15]、[16]、[17]、[18] 、[19]是打印gc日志,其中[16]在jdk1.8之后无需设置

12. [20]、[21]则是内存溢出时dump堆

 

 

转载于:https://my.oschina.net/124259473/blog/1924267

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值