JVM - 垃圾收集器

如果感觉对您有帮助,那就一键三连吧!关注公众号(星翰成长日记),及时获取更新

在选择垃圾回收器之前,我们必须先了解评价它们的三大核心维度。吞吐量、延迟、内存占用。这是评估JVM垃圾回收器性能的三个黄金维度,理解这三个维度及其相互关系,是进行JVM调优和垃圾回收器选型的核心基础。

吞吐量

衡量的是JVM在处理用户任务(非GC工作)上的效率。它关注的是:在总时间里面,有多少时间是真正干活的。计算公式为:吞吐量 = 运行用户代码的时间 / (运行用户代码的时间 + 垃圾收集总时间)
比如一个应用运行了100分钟,在这期间,垃圾回收总共花费了1分钟,那么它的吞吐量就是:(100 - 1)/ 100 = 99%,这意味着CPU有99%的时间在执行你的业务逻辑,而只有1%的时间在执行GC。

不难看出,吞吐量越高,越适合后台运算、批处理、科学计算等场景。在这些场景中,用户不关心单次请求的快慢,只关心在有限的时间内完成了多少次任务。比如一个夜间报表生成任务,即使GC停顿了几分钟,只要能在几小时内完成海量任务,就是成功的。

延迟

又叫做响应时间。是指一个应用程序线程因为等待垃圾回收活动完成而无法继续执行其任务的总时间。延迟的本质是Stop The World,核心是衡量和控制这个“静止”状态的持续时间。

Stop The World:在垃圾回收的某些特定阶段,为了保证内存状态的一致性,JVM会暂停所有正在执行的应用程序。在这个阶段,应用程序会像被整个按下“暂停键”一样,直到GC线程完成它的工作。这段暂停时间就是我们所说的GC停顿STW时间

内存占用

指为了实现特定的GC性能目标(高吞吐量或低延迟),JVM需要额外使用的内存空间。一个GC算法如果为了达到性能目标而需要消耗大量额外内存,可能会导致整体成本上升。通常与吞吐量和延迟成反比。

由上不难看出,吞吐量越高越好,越高意味着GC占用资源少,应用能处理更多业务;延迟越低越好,特别是需要用户交互的应用,长时间停顿会导致卡顿甚至超时。但吞吐量和延迟是相互矛盾的,在有限的硬件资源下,优化其中一个指标,必然会牺牲另外一个指标为代价,鱼和熊掌不可兼得。

垃圾收集器详解

下图展示了各代垃圾收集器的组合关系、目标侧重及适用场景,是进行技术选型的路线图。

各个垃圾收集器关系图

Serial 收集器

Serial收集器是垃圾回收器家族的“老祖宗”,采用标记-复制算法,是一个“单线程”的垃圾收集器。这里的“单线程”有两层含义:

  1. GC工作时,仅适用一个CPU核心:在垃圾回收时,它会暂停所有用户线程(STW),并使用一个单独的GC线程来完成所有的垃圾回收工作。
  2. 任务单一:只能用于新生代的垃圾回收。与之配套的老年代收集器是它的“大哥”-Serial Old。

Serial实现简单,逻辑清晰,是学习和理解GC原理的最佳入门案例。因为不需要和其他线程协作,也不需要维护复杂的并发数据结构,额外内存占用开销是所有收集器里最低的。单缺点也很明显,由于是单线程,STW时间长;在现代多核服务器上,无法利用多核优势来加速GC过程,造成CPU资源的巨大浪费。

ParNew 收集器

ParNew收集器实际上是Serial收集器的多线程并行版本,也是使用标记-复制算法。它的设计目标非常明确:在多核CPU环境下,利用多线程优势来加速新生代的垃圾回收过程,从而缩短STW时间,即关注于延迟。

ParNew本身并不是一个独立、有长远规划的收集器,只能用于新生代的回收,它的诞生主要是为了配合CMS收集器。在进行回收时,GC控制器根据CPU核心数,启动N个工作线程(与CPU核心数一样多的线程),这N个GC线程并行地从各自的GC Roots任务集出发,对新生代对象进行标记和复制。

ParNew的核心价值是在多核环境下显著缩短了Minor GC时间。本质仍然是STW,同时引入了线程切换和同步的开销;同时受限于CMS。

Parallel Scavenge 收集器

Parallel Scavenge收集器也是一个多线程并行的垃圾收集器,同样也只能用于新生代的回收,采用标记-复制算法。但Parallel Scavenge的目标是:尽可能的提高应用程序的吞吐量。追求在单位时间内,能够执行更多的用户业务逻辑,至于单次GC会不会让用户线程等的久一点,它不太关心。

Parallel Scavenge 最独特、最核心的特性是它提供了一个自适应调节策略。这个策略默认开启,JVM不再被动的等待用户手动设置新生代的大小、Eden区与Survivor区的比例等参数,它会动态的监控GC的各项性能指标,并根据预设的目标,自动的、智能的调整堆中各区域的大小,以及晋升老年代的年龄阈值等参数。这个特性极大的简化了GC调优工作,对于不熟悉GC内部细节的开发者来说,只需设定一个宏观的目标,JVM就能自动的找到一个合理的配置,省去了手动反复试验的烦恼。

Serial Old 收集器

Serial Old是Serial 收集器的老年代版本,设计理念和Serial一脉相承:单线程和STW。采用标记-整理算法,主要职责是作为Serial收集器的官方搭档,组成Serial + Serial Old组合,也是CMS收集器在发生“并发模式失败”时的后备预案。

Parallel Old 收集器

Parallel Old是Parallel Scavenge收集器的老年代版本,采用标记-整理算法,设计目标两者完全一致。这对组合的诞生意义重大,从新生代到老年代,全程利用多核优势,最大限度的压榨CPU的计算能力,实现了真正的吞吐量优先,它的出现,标志着JVM拥有了第一个真正意义上的、完整的多线程并行的整堆垃圾回收解决方案,被确立为JDK8的默认GC策略

CMS 收集器

全称是Mostly Concurrent Mark and Sweep,即“主要与用户线程并发的标记-清除”算法。采用标记-清除算法。核心目标是尽可能的缩短垃圾回收的停顿时间,提供低延迟服务。工作流程比上述其他收集器都要复杂,分为如下四个阶段:

初始标记:此阶段需要STW。标记出所有从GC Roots能直接关联到的对象。因为直接关联的对象比较少,这个阶段速度极快,停顿时间非常短。

并发标记:这个阶段与用户线程并发执行。从初始标记阶段标记的对象开始,遍历整个对象图,递归的标记所有可达对象。这是GC工作中最耗时的阶段,但由于和用户线程并发执行,所以不会造成程序的停顿。用户线程在标记的同时可以继续创建新对象、修改引用关系。

重新标记:此阶段需要STW。修正在并发标记期间,用户程序因为程序运行而导致标记产生变动的那一部分对象,比如一个原本存活的对象在并发标记期间变成了垃圾,或者一个原本是垃圾的对象被重新引用了。主要目的是为了修正并发标记期的偏差。通常速度比并发标记快很多,但STW会比初始标记长,但依然远短于一次完整的Full GC。

并发清除:与用户线程并发执行。清理掉被标记判断为死亡的对象,回收内存空间。

CMS的设计在当时是革命性的。通过将最耗时的标记和清除工作与用户线程并发执行,极大的缩短了老年代GC的STW时间,通常为数百毫秒,使其在互联网应用中可行。但缺点也很明显:

对CPU资源敏感:在并发阶段,GC线程会占用一部分CPU核心(默认是(CPU核

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值