本篇针对第二个问题:如何对垃圾进行回收-具体实现
image.png-
前置知识
- safepoint:安全点
简单理解:就是JVM当前正在运行的线程状态可以确定的一个时刻,睡眠或阻塞的线程在这一个安全区域(Safe Region)中(引用关系不会发生变化了)。如此,才能确定oopMap-》然后确定GC Roots,最终才可以发起GC。
- safepoint:安全点
- stop the world:所有线程暂停下来,不再运行,引用关系也就不会发生变化了,所以能进行GC。如果GC过程中,引用关系还在变化,这样的GC肯定是不准确的,会严重影响到系统的正确执行。
-
垃圾收集器
-
serial收集器:单线程收集。JVM团队的目标就是不断缩减停顿时间。对于单CPU应用,这是很棒的,单线程下工作效率很高,适用于Client模式。
image.png
-
-
ParNew收集器:多线程收集(用户线程依然不可以继续执行)。除了是多线程收集之后,其余都和serial收集器一样。在单CPU情况下,serial肯定是可以秒杀ParNew收集器的,但是在多CPU的情况下,多线程就会显得效率更高了。ParNew适合用在server模式下。注意:在和老年代收集器CMS配合使用下,只有ParNew和Serial可以与其配合使用。
-
Parallel Scavenge收集器:"多线程收集器"、"吞吐量优先收集器",与用户交互的程序是更关注停顿时间的,但是像计算任务却是更关注吞吐率(运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间) )。
参数: MaxGCPauseMills:尽可能的控制GC时间小于这个值,不过会影响到吞吐率和GC频率。
GCTimeRatio:如果设置为19,则垃圾收集占的比例就是1/(1+19) = 5%
自适应调节参数UseUseAdapiveSizePolicy:将这个开关参数打开,JVM就会根据系统运行情况分配新生代三区配比 -
Serial Old收集器:"单线程收集器"、"老年代收集器"。适用场景:1.适用于Client模式下 2.作为CMS收集发生Concurrent Mode Failue的时候作为预案使用。
image.png -
Parallel Old收集器:"多线程收集器"、"吞吐量优先收集器"、"新生代老年代唯一适配的黄金搭档"。和Parallel Scavenge配合使用作为吞吐量优先收集器的最佳方案。在此之前,因为Parallel Scavenge收集器只能和Serial Old收集器配合使用,但Serial在server模式下无法发挥出多CPU的优势,而成为性能瓶颈。
image.png
!!!!!!CMS收集器
-
CMS收集器:"并行收集器"、"标记-清除算法"。追求停顿时间短短短!可以与用户线程并行的工作。根据图片我们可以知道,CMS收集器工作分为四个阶段。
初始标记:Stop The World。简单标记一下GC Roots能引用到的对象。时间很短。
并发标记:并行。完整的进行标记,与用户线程同时工作。
重新标记:Stop The World。修正并发标记阶段发生引用变化那部分对象的记录。远比并发标记时间短,比初始标记长。
并发清理:内存回收。无法清理浮动垃圾(因为内存清理的时候用户线程还在运行,所以还会产生垃圾)。CMS收集器会产生的问题:
问题1:对CPU资源敏感。CMS默认启动的垃圾收集线程数:(CPU数量+3)/4,可想而知,当CPU数量=4的时候,就要一个垃圾收集线程,占了25%的CPU资源。不过当CPU数量越多的时候,占用比例就不明显了。
问题2:"Concurrent Mode Failure"问题,设置老年代触发垃圾收集比例,当老年代触发垃圾收集比例设置的过高的时候就可能导致在垃圾收集的时候用户线程没有足够的内存空间来运行,就会出现"Concurrent Mode Failure"失败,JVM将使用serial old收集器来进行老年代的回收。
问题3:"标记-清除"算法的缺点,产生大量内存碎片。JVM给了我们两个选择,一是设置一个参数当进行多少次GC的时候就进行一次带压缩的Full GC,二是设置一个在要进行Full gc的时候先来一次内存整理。
问题4:就是上述所说的浮动垃圾的清理问题,只能留待下一次GC的时候清理。
image.png
网友评论