老年代回收容器——CMS
   
    
    
    1、CMS工作原理
   
    一般老年代我们选择的垃圾回收器是CMS,他采用的是
    
     标记清理算法
    
    (不是标记整理)。之前提到过
    
     Stop the World
    
    状态,就是垃圾回收时停止一切线程的工作,如果在这个状态下再去慢慢执行标记清理算法,会导致系统卡死时间过长,所以CMS垃圾回收器采取的是
    
     垃圾回收线程和系统工作线程尽量同时执行的模式
    
    来处理的。
   
    
     工作原理
    
    :为了避免长时间
    
     Stop the World
    
    ,CMS采用了4个阶段来垃圾回收,分别是初始标记、并发标记、重新标记和并发清理。其中初始标记和重新标记,耗时很短,虽然会导致
    
     Stop the World
    
    ,但是影响不大,然后并发标记和并发清理,两个阶段耗时最长,但是是可以跟系统的工作线程并发运行的,所以对系统没太大影响。
   
    
    
    2、CMS垃圾回收的四个阶段
   
    
    
    (1)初始标记——标记直接GC Roots(直接)
   
    在这个阶段让系统的工作线程全部停止,进入
    
     Stop the World
    
    状态。同时
    
     标记所有GC Roots直接引用的对象
    
    ,是直接引用!比如下面这段代码,仅仅会通过
    
     replicaManager
    
    这个类的静态变量代表的GC Roots,去标记出来他直接引用的
    
     ReplicaManager
    
    对象,不会去管
    
     ReplicaFetcher
    
    这种对象,因为
    
     ReplicaFetcher
    
    对象是被
    
     ReplicaManager
    
    类的
    
     replicaFetcher
    
    实例变量引用的。(之前说过,方法的局部变量和类的静态变量是GC Roots。但是类的实例变量不是GC Roots。)
   
public class Kafka {
    private static ReplicaManager replicaManager = new ReplicaManager();
}
public class ReplicaManager {
    private ReplicaFetcher replicaFetcher = new ReplicaFetcher();
}
    ![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fk34GSB4-1581153515938)(/Users/zhangye/Library/Application Support/typora-user-images/image-20191023210028357.png)]](https://img-blog.csdnimg.cn/20200208172012902.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDQyNDY2OA==,size_16,color_FFFFFF,t_70) 
   
初识标记如图所示
    
    
    (2)并发标记——对老年代所有对象进行GC Roots追踪(最耗时)
   
    这个阶段会让系统线程可以随意创建各种新对象,继续运行。在运行期间可能会创建新的存活对象,也可能会让部分存活对象失去引用,变成垃圾对象。在这个过程中,垃圾回收线程,会尽可能的对已有的对象进行GC Roots追踪。GC Roots追踪,意思就是对类似
    
     ReplicaFetcher
    
    之类的全部老年代里的对象,他会去看他被谁引用了,
    
     认定为是被GC Roots间接引用后,就不需要回收它。因为老年代里存活对象是比较多的,这个过程会追踪大量的对象,所以耗时较高。
    
   
    ![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4a1EWItV-1581153515940)(/Users/zhangye/Library/Application Support/typora-user-images/image-20191023210352901.png)]](https://img-blog.csdnimg.cn/20200208172036481.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDQyNDY2OA==,size_16,color_FFFFFF,t_70) 
   
    
    
    (3)重新标记
   
    第二阶段里,你一边标记存活对象和垃圾对象,一边系统在不停运行创建新对象,让老对象变成垃圾,所以第二阶段结束之后,绝对会有很多存活对象和垃圾对象,是之前第二阶段没标记出来的。在这个阶段,要再次进入
    
     Stop the World
    
    阶段,重新标记下在第二阶段里新创建的一些对象,还有一些已有对象可能失去引用变成垃圾的情况。重新标记的阶段只是对变动过的
    
     少数对象
    
    进行标记,是速度很快的
   
    
    
    (4)并发清理
   
这个阶段就是让系统程序随意运行,然后清理掉之前标记为垃圾的对象即可,也是很耗时的。
    ![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cfWztAVu-1581153515941)(/Users/zhangye/Library/Application Support/typora-user-images/image-20191023210811272.png)]](https://img-blog.csdnimg.cn/20200208172107745.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDQyNDY2OA==,size_16,color_FFFFFF,t_70) 
   
    
    
    3、CMS性能分析
   
    
    
    (1)好的方面
   
    CMS的第二阶段和第四阶段,都是很耗时的,但都和系统程序是并发执行的,所以基本这两个最耗时的阶段对性能影响不大。只有第一个阶段和第三个阶段是需要
    
     Stop the World
    
    的,但是这两个阶段都是简单的标记而已,速度非常的快,所以基本上对系统运行响应也不大。
   
    
    
    (2)坏的方面
   
    
    
    ①并发回收导致CPU资源紧张。
   
并发标记和并发清理两个最耗时的阶段,使垃圾回收线程和系统工作线程同时工作,导致有限的CPU资源被垃圾回收线程占用了一部分。在这两个阶段,CMS的垃圾回收线程是比较耗费CPU资源的。CMS默认启动的垃圾回收线程的数量是(CPU核数 + 3)/ 4,比如的2核4G机器,就会占用(2+3)/4 = 1个CPU被用来垃圾回收。
    
    
    ②Concurrent Mode Failure问题
   
    在并发清理阶段,CMS只不过是回收之前标记好的垃圾对象,但这个时候系统一直在运行,先把某些对象分配在新生代,然后可能触发了一次Minor GC,一些对象进入了老年代,在短时间内又没人使用这些对象,这种垃圾对象就是
    
     浮动垃圾
    
    ,虽然它是垃圾,但是不会回收他们,要等到下一次才能回收。
   
    
     CMS垃圾触发的时机是当老年代内存占用到达一定比例时,就会自动GC
    
    ,
    
     -XX:CMSInitiatingOccupancyFaction
    
    这个参数可以设置老年代内存占用到多少比例时触发垃圾回收。JDK 1.6默认是92%。预留8%的空间给并发回收期间,系统程序把一些新对象放入老年代中。如果垃圾回收期间,要放入的对象大于可用内存空间,就会发生
    
     Concurrent Mode Failure
    
    ,即并发垃圾回收失败了,我一边回收,你一边把对象放入老年代,内存都不够了。此时就会自动用
    
     Serial Old
    
    垃圾回收器替代CMS,就是直接强行把系统程序
    
     Stop the World
    
    ,重新进行长时间的GC Roots追踪,标记出来全部垃圾对象,不允许新的对象产生。
   
    
    
    ③内存碎片问题
   
    老年代的CMS采用
    
     标记清理
    
    算法(不是
    
     标记整理
    
    ),每次都是标记出来垃圾对象,然后一次性回收掉,这样会导致大量的内存碎片产生,太多的内存碎片实际上会导致更加频繁的Full GC。
   
    CMS有一个参数是
    
     -XX:+UseCMSCompactAtFullCollection
    
    ,默认是打开的,意思是在Full GC之后要再次进行
    
     Stop the World
    
    ,停止工作线程,然后进行碎片整理,就是把存活对象挪到一起,空出来大片连续内存空间,避免内存碎片。
   
    还有一个参数是
    
     -XX:CMSFullGCsBeforeCompaction
    
    ,这个意思是执行多少次Full GC之后再执行一次内存碎片整理的工作,默认是0,意思就是每次Full GC之后都会进行一次内存整理,存活对象都放在一起,然后空出来大片连续内存空间可供使用。
   
    ![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LFVPZWhV-1581153515942)(/Users/zhangye/Library/Application Support/typora-user-images/image-20191023214004334.png)]](https://img-blog.csdnimg.cn/20200208172233416.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDQyNDY2OA==,size_16,color_FFFFFF,t_70)
    
    红圈处即浮动垃圾
   
 
