zl程序教程

您现在的位置是:首页 >  其他

当前栏目

深入剖析垃圾收集器之后,我发现里面没有扫帚

2023-03-14 22:58:06 时间

前言



为了面试实习生,把垃圾收集器相关的知识整理一下。


一、前置的几个概念



1、STW(Stop the world)


它是指 JVM 由于要执行 GC 而停止了应用程序的执行,并且这种情形会在任何一种 GC 算法中发生。当 Stop-the-world 发生时,除了 GC 的线程以外,其他的线程均处于等待的状态,直到 GC 任务完成。


实际上,很多 GC 优化都是通过减少 Stop-the-world 的时间来提高程序的性能。


2、Safe-point


程序执行时并非在所有地方都能停顿下来开始 GC,只有在某些特定的位置才可以,这些特定的位置被称为安全点(Safe-point)。在使用 GC roots 分析可达性时,引用关系不会发生改变的点就是安全点,常用的安全点有如下几种:


 方法调用、循环跳转、异常跳转


3、JVM 运行模式


JVM 运行模式有如下这两种:

Client:启动快,进入稳定期后运行速度不如 Server 快。


Server:启动慢,进入稳定期后运行速度优于 Client . Server 模式采用的是重量级的虚拟机,对程序会进行更多的优化。


二、垃圾收集器的简单介绍 



20190701114809997.png


1、新生代使用的垃圾收集器 


(1) Serial


Serial 即串行的意思,也就是说它以串行的方式执行,它是单线程的收集器,只会使用一个线程进行垃圾收集工作,GC 线程工作时,其它所有线程都将停止工作。


它的优点是简单高效,在单个 CPU 环境下,由于没有线程交互的开销,因此拥有最高的单线程收集效率,所以,它是 Client 场景下的默认新生代收集器。


(2)PawNew


就是 Serial 收集器的多线程版本,但要注意一点,ParNew 在单核环境下是不如 Serial 的,在多核的条件下才有优势。 


(3)Parallel Scavenge


同样是多线程的收集器,其它收集器目标是尽可能缩短垃圾收集时用户线程的停顿时间,而它的目标是提高吞吐量(吞吐量 = 运行用户程序的时间 / (运行用户程序的时间 + 垃圾收集的时间))。


停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验。而高吞吐量则可以高效率地利用 CPU 时间,尽快完成程序的运算任务,适合在后台运算而不需要太多交互的任务。


2、老年代使用的垃圾收集器 


(1)Serial Old 


Serial 收集器的老年代版本,Client 场景下默认的老年代垃圾收集器。 

使用标记-整理算法清除老年代垃圾


(2)Parallel Old 


Parallel Scavenge 收集器的老年代版本。在注重吞吐量的场景下,可以采用 Parallel Scavenge + Parallel Old 的组合。


使用标记-整理算法收集老年代垃圾。


3)CMS(Concurrent Mark Sweep)


一般将PawNew与CMS配合使用。

使用标记-清除算法收集老年代垃圾。


3、G1(Garbage-First)


G1收集器是一款面向服务端应用的垃圾收集器,在多 CPU 和大内存的场景下有很好的性能。HotSpot 开发团队赋予它的使命是未来可以替换掉 CMS 收集器。


使用复制 + 标记 - 整理算法收集新生代和老年代垃圾。


G1 把堆划分成多个大小相等的独立区域(Region),新生代和老年代不再物理隔离。


三、CMS详解 



1、工作流程 


(1)初始标记: 仅仅只是标记一下 GC Roots 能直接关联到的对象,速度很快,需要停顿(Stop-the-world)

(2)并发标记: 进行 GC Roots Tracing 的过程,它在整个回收过程中耗时最长,不需要停顿

(3)重新标记: 为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,需要停顿(Stop-the-world)

(4)并发清除: 清理垃圾,不需要停顿


2、优缺点


优点:


在整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,不需要进行停顿。 


缺点:

  •         吞吐量低
  •         无法处理浮动垃圾
  •         标记 - 清除算法带来的内存空间碎片问题 


四、G1收集器详解 



1、工作流程 


(1)初始标记:仅仅只是标记一下GC Roots能直接关联到的对象,并且修改TAMS指针的值,让下一阶段用户线程并发运行时,能正确地在可用的Region中分配新对象。这个阶段需要停顿线程(STW),但耗时很短,而且是借用进行Minor GC的STW的时候同步完成的,所以G1收集器在这个阶段实际并没有额外的停顿;


(2)RootRegion扫描,扫描GCroot所在的region到Old区的引用;


(3)并发标记:类似CMS,但也是整个堆,而不是只有old区;


(4)重新标记:类似CMS,但也是整个堆,并且上一步中的X区被删除。另外采用了初始标记阶段的SATB,重新标记的速度变快;


(4)复制/清理:选择所有Y区reigons和'对象存活率较低'的O区regions组成Csets,进行复制清理。

 

 2、G1提高效率的点有哪些 


(1)重新标记时X区域直接删除;

(2)Rset降低了扫描的范围,上题中两点;

(3) 重新标记阶段使用SATB速度比CMS快;

(4) 清理过程为选取部分存活率低的Region进行清理,不是全部,提高了清理的效率。 


3、对比CMS,有哪些不同 

       

(1) region化的内存结构,采用复制清理的方式,避免了内存碎片。但是这种清理也造成了STW;

(2) SATB速度更快;

(3) 初始标记,并发标记,重新标记,清理垃圾四个阶段很像,但是G1中有很多标记region的操作,并借助Rset进行了范围的缩小,提高了并发标记的速度。小结下就是初始标记和YGC的STW一起了,提高了效率;并发标记因为rset的设计,扫描范围缩小了,提高了效率;重新标记因为使用了SATB提高了效率;清理虽然造成了STW,但是复制使内存紧凑,避免了内存碎片。同时只清理垃圾较多的region,最大限度的降低了STW时间。


参考:

JVM几种常见的垃圾收集器总结_To be a great coder-CSDN博客_垃圾收集器

notebook/java_jvm垃圾收集器.md at master · sunwu51/notebook · GitHub