11.内存回收的具体实现——垃圾收集器

1.Serial收集器

最基本的垃圾收集器,一种单线程收集器,在GC时必须STW

缺点:

必须STW,并且STW时间占比太高

优点:

简单高效(与其他垃圾收集器的单线程模式相比)
对于桌面应用在虚拟机中使用的只有几十兆乃至一两百兆的新生代内存来说,stw的停顿时间在最多一百毫秒以内,完全可以接受

示例图:

在这里插入图片描述

2.ParNew收集器

是Serial的多线程版本
参数,
收集算法,
STW对象分配规则,
回收策略
都和Serial完全一样
ParNew收集器除了多线程以外和Serial收集器并没有太多创新的地方,但是它却是Server模式下的虚拟机首选的新生代收集器,其中有一个很重要的和性能无关的原因是,除了Serial收集器外,目前只有它能与CMS收集器配合工作。CMS收集器是一款几乎可以认为有划时代意义的垃圾收集器,因为它第一次实现了让垃圾收集线程与用户线程基本上同时工作。ParNew收集器在单CPU的环境中绝对不会有比Serial收集器更好的效果,甚至由于线程交互的开销,该收集器在两个CPU的环境中都不能百分之百保证可以超越Serial收集器。当然,随着可用CPU数量的增加,它对于GC时系统资源的有效利用还是很有好处的。它默认开启的收集线程数与CPU数量相同,在CPU数量非常多的情况下,可以使用-XX:ParallelGCThreads参数来限制垃圾收集的线程数。

示例图:

3. Parallel Scavenge收集器

Parallel Scavenge收集器也是一个新生代收集器,也是用复制算法的收集器,也是并行的多线程收集器,但是它的特点是它的关注点和其他收集器不同。

CMS等收集器的关注点是尽可能缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量。

吞吐量:

所谓吞吐量的意思就是CPU用于运行用户代码时间与CPU总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间),虚拟机总运行100分钟,垃圾收集1分钟,那吞吐量就是99%。

适用范围:

停顿时间短适合需要与用户交互的程序,良好的响应速度能提升用户体验;高吞吐量则可以高效率利用CPU时间,尽快完成运算任务,主要适合在后台运算而不需要太多交互的任务。虚拟机提供了-XX:MaxGCPauseMillis和-XX:GCTimeRatio两个参数来精确控制最大垃圾收集停顿时间和吞吐量大小。不过不要以为前者越小越好,GC停顿时间的缩短是以牺牲吞吐量和新生代空间换取的。
由于每次GC也有开销,多次频繁的GC会使单次停顿时间变小但总GC消耗时间会增加,所以会使吞吐量变小
由于与吞吐量关系密切,Parallel Scavenge收集器也被称为“吞吐量优先收集器”。Parallel Scavenge收集器有一个-XX:+UseAdaptiveSizePolicy参数,这是一个开关参数,这个参数打开之后,就不需要手动指定新生代大小、Eden区和Survivor参数等细节参数了,虚拟机会根据当前系统的运行情况手机性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量。如果对于垃圾收集器运作原理不太了解,以至于在优化比较困难的时候,使用Parallel Scavenge收集器配合自适应调节策略,把内存管理的调优任务交给虚拟机去完成将是一个不错的选择。

示例图:

在这里插入图片描述

4.Serial Old 收集器

Serial 的老年代版本,同样是一个单线程收集器,使用“标记-整理算法”

用途:

在JDK1.5及以前的版本中与Parallel Scavenge搭配使用
作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用

5.Parallel Old 收集器

Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和”标记-整理“算法。
这个收集器是在JDK1.6中才开始提供的,在此之前,新生代的Parallel Scavenge收集器一直处于比较尴尬的状态。原因是新生代如果选择了Parallel Scavenge收集器,老年代除了Serial Old收集器别无选择(因为它无法与CMS配合使用)
Serial Old是一个单线程收集器,在CPU多线程的环境下他无法达到很好的老年代GC效率,这样,在Serial Old的拖累下,Parallel Scavenge也无法达到想要的吞吐量优先的效果
事实上,在老年代很大并且硬件比较高级的情况下,Parallel Scavenge+Serial Old的组合还不如ParNew + CMS的吞吐量大
直到jdk1.6以后Parallel Old 的出现,Parallel Scavenge+Parallel Old才能称得上名副其实的 吞吐量优先 收集器组合

示例图:

在这里插入图片描述

6.CMS收集器

CMS(Conrrurent Mark Sweep)收集器是以获取最短回收停顿时间为目标的收集器。使用标记 - 清除算法,收集过程分为如下四步:

工作过程:

  1. 初始标记,标记GCRoots能直接关联到的对象,时间很短,需要STW
  2. 并发标记,进行GCRoots Tracing(可达性分析)过程,时间很长。
  3. 重新标记,修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录。比初始标记阶段稍长一些,但远比并发标记时间短。需要STW
  4. 并发清除,回收内存空间,时间很长。

示例图:

在这里插入图片描述
其中,并发标记与并发清除两个阶段耗时最长,但是可以与用户线程并发执行。

由于整个过程中耗时最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作,所以,从总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的

缺点:

  1. CMS收集器对CPU资源非常敏感。
    因为在并发阶段,它会占用了一部分线程(或者说CPU资源)而导致应用程序变慢,总吞吐量会降低。
  2. CMS收集器无法处理浮动垃圾,可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。
    由于CMS并发清理阶段用户线程还在运行着,伴随着程序运行自然就还会有新的垃圾不断产生,这部分垃圾出现在标记过程之后,CMS无法在当次收集中处理它们,只好留待在下一次GC时再清理掉,这一部分垃圾就称为“浮动垃圾”。
  3. Concurrent Mode Failure错误
    因为CMS在垃圾收集阶段用户程序还需要继续执行,所以需要预留一部分内存空间供用户程序使用,在JDK1.6中这个阈值被默认设置为老年代在使用了92%内存空间时CMS开始执行,如果预留的8%不够用户线程使用就会抛出Concurrent Mode Failure错误,在这时虚拟机就将采用Serial Old收集器来重新进行老年代的垃圾收集,这样STW的时间就会很长
  4. CMS是一款基于“标记-清除”算法实现的收集器,这意味着收集结束时会有大量的空间碎片产生。
    为了解决这个问题,CMS收集器提供了一个-XX:+UseCMSCompactAtFullColletion开关参数(默认是开启的),用于在CMS收集器顶不住要进行Full GC时开启内存碎片合并整理过程,内存整理的过程是无法并发的,空间的碎片问题没有了,但停顿的时间不得不变长了。虚拟机设计者还提供了另外一个参数-XX:CMSFullGCsBeforeCompaction,这个参数是用于设置执行多少次不压缩的Full GC后,跟着来一次带压缩的(默认值为0,表示每次进入Full GC时都进行碎片整理)。

7.G1收集器

G1垃圾回收器(G1 Garbage Collector)。

G1是目前技术发展的最前沿成果之一,HotSpot开发团队赋予它的使命是未来可以替换掉JDK1.5中发布的CMS收集器。与其他GC收集器相比,G1收集器有以下特点:

(1). 并行和并发。使用多个CPU来缩短Stop The World停顿时间,与用户线程并发执行。

(2). 分代收集。独立管理整个堆,但是能够采用不同的方式去处理新创建对象和已经存活了一段时间、熬过多次GC的旧对象,以获取更好的收集效果。

(3). 空间整合。基于标记 - 整理算法,无内存碎片产生。

(4). 可预测的停顿。能简历可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒。

在G1之前的垃圾收集器,收集的范围都是整个新生代或者老年代,而G1不再是这样。使用G1收集器时,Java堆的内存布局与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分(可以不连续)Region的集合。

示例图:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_24516549/article/details/88385687
今日推荐