七、垃圾收集中级

请添加图片描述

JVM由浅入深系列


JVM由浅入深系列
一、关于Java性能的误解
二、Java性能概述
三、了解JVM概述
四、探索JVM架构
五、垃圾收集基础
六、HotSpot中的垃圾收集
七、垃圾收集中级
八、垃圾收集高级

👋垃圾收集中级

⚽️1. 权衡收集器插件

就 Java 平台而言,有一点可能初学者未必能马上意识到,即尽管 Java 有垃圾收集器,但是语言和虚拟机规范都没有说明应该如何实现垃圾收集。事实上,曾经有一些 Java 实现(例如 Lego Mindstorms)根本没有实现任何类型的垃圾收集!

在 Sun(现在是 Oracle)的环境中,垃圾收集子系统被视为一个可插拔的子系统。这意味着同一个 Java 程序可以使用不同的垃圾收集器来执行,而不改变程序的语义,尽管它的性能可能会根据使用的收集器而有很大的不同。

使用可插拔的收集器的主要原因是垃圾收集是一种通用的计算技术。特别是,同样的算法未必适用于每个工作负载。由于人们关注点不同,有些还是相互冲突的,因此垃圾收集算法代表了一种妥协或权衡。

在选择垃圾收集器时,开发人员通常需要考虑以下主要问题:
• 暂停时间(也叫暂停长度或持续时间);
• 吞吐量(垃圾收集时间占应用程序运行时间的百分比 );
• 暂停频率(收集器需要停止应用程序的频率);
• 回收效率(一个垃圾收集工作周期内可收集的垃圾量);
• 暂停的一致性(所有的暂停长度是否大致相同)。

其中,暂停时间往往会引起过多的关注。尽管暂停时间对许多应用程序确实很重要,但也不应该孤立地考虑它。

例如,高度并行的批处理或大数据应用程序可能更关注吞吐量,而不是暂停时间的长短。对于很多批处理作业来说,暂停时间就算有几十秒也没有多大关系。因此,与不计成本地降低暂停时间的算法相比,应该首选 CPU 效率和吞吐量更高的垃圾收集算法。

性能工程师还需要注意的是,在考虑收集器的选择时,有时还会有一些其他的权衡和顾
虑。在 HotSpot 的情况下,选择还受制于可用的收集器。

截至 Java 10,在 Oracle/OpenJDK 内有 3 种主流的收集器供一般性生产环境使用。我们已经见过并行(又称吞吐量)收集器,从理论和算法的角度看它们是最容易理解的。本章会介绍另外两种主流的收集器,并解释它们与并行垃圾收集的区别。

⚽️2. 并发垃圾收集理论

在专门的系统中,例如图形或动画显示系统,通常有一个固定的帧率,这为执行垃圾收集提供了一个有规律的、固定的机会。

然而,通用的垃圾收集器并没有这样的领域知识来提高暂停时间的确定性。更为糟糕的是,不确定性是由分配行为直接导致的,而很多使用 Java 的系统显示出高度可变的分配行为。

这种安排的次要缺点是本身的计算被延迟了;其主要缺点是这些垃圾收集穿插而来的不确定性。——Edsger Dijkstra

现代垃圾收集理论的出发点就是试图解决 Dijkstra 所提出的问题,即 STW 暂停的不确定性
(无论是持续时间还是频率)是使用垃圾收集技术的主要烦恼。

一种解决方法是使用一个并发(至少部分并发,或大部分并发)的收集器,在应用程序线程运行的同时做一些收集所需的工作,以减少暂停时间。这不可避免地降低了用于应用程序实际工作的计算能力,同时也使执行收集所需的代码复杂化了。

不过在讨论并发收集器之前,还需要介绍一个重要的垃圾收集术语和技术,因为它对理解现代垃圾收集器的性质和行为至关重要。

⚾️2.1 JVM安全点

为了执行STW垃圾收集,比如HotSpot的并行收集器所执行的那些,必须停止所有的应用程序线程。看上去我们在反复说同样的事情,但直到现在我们还没有讨论JVM到底是如何实现这一点的。

实际上,JVM并不是一个完全抢占式的多线程环境。

这并不意味着它是一个纯粹的合作式环境,恰恰相反,操作系统仍然可以在任何时候抢占线程(从某个 CPU 核心上移除一个线程)。例如当一个线程用尽了其时间片,或将自己放 wait() 中时,就会出现这种情况。

除了这个核心的操作系统功能外, JVM 还需要执行协调操作。为了实现这一点,运行时就要求每个应用程序线程都有称为安全点(safepoint)的特殊执行点,其中线程的内部数据结构处于已知的良好状态。此时线程能被挂起,以执行协调操作。

要理解安全点的意义,可以考虑一个完全 STW 垃圾收集器的情况。为了使之运行,它需要一个稳定的对象图。这意味着所有的应用程序线程都必须暂停,因为垃圾收集线程无法要求操作系统对应用程序线程强制执行这个要求,所以必须有应用程序线程(它作为 JVM进程的一部分执行)的配合才能实现。 JVM 对安全点的实现方式受限于以下两个主要规则:

• JVM 不能强制一个线程进入安全点状态;
• JVM 可以阻止一个线程离开安全点状态。

这意味着当需要安全点时, JVM 解释器的实现必须包含在屏障(barrier)处释放(yield)的代码。对于 JIT 编译的方法,必须在生成的机器码中插入等效的屏障。到达安全点的一般情况是以下这样的。

  1. JVM 设置一个全局的“安全点时间”(time to safepoint) 标志。
  2. 单个的应用程序线程轮询并查看该标志已设置。
  3. 暂停并等待被再次唤醒。

当这个标志被设置后,所有的应用程序线程都必须停止,而且停得快的线程必须等待停得慢的,这个时间可能不会被完全统计到暂停时间中。

普通的应用程序线程使用这种轮询机制:在解释器中,它们总是会在执行任何两个字节码之间进行检查。如果是编译后的代码,最常见的一种情况是 JIT 编译器在退出编译方法的地方插入一个安全点轮询,另一种情况是循环分支向后跳转时(比如,回到循环的顶部)插入一个安全点轮询。

一个线程可能要花很长时间才到安全点,甚至理论上可能永远不会停止,但这是一种病态的情况,一定是有意而为之的。

所有的线程都必须在 STW 阶段开始之前完全停止,这个想法类似于使用锁存器,比如用 java.utilite.concurrent 库中的 CountDownLatch 实现的锁存器。

这里值得一提的是安全点条件的一些特殊情况。在如下情况下,一个线程会自动处于安全点状态:

• 阻塞在一个管程(monitor)上;
• 正在执行 JNI 代码。

在如下情况下,一个线程未必处于安全点状态:

• 在执行字节码的过程中(解释模式);
• 已经被操作系统中断。

稍后还会再讲到安全点机制,因为它是 JVM 内部运作的关键一环。

⚾️2.2 三色标记

Dijkstra 和 Lamport 在 1978 年发表的论文中描述了三色标记算法,该论文对证明并发算法和垃圾收集的正确性具有里程碑式的意义,其中所描述的基本算法至今仍是垃圾收集理论的重要组成部分。

三色标记算法的工作原理如下:
• 垃圾收集根标记为灰色;
• 所有其他对象都标记为白色;
• 有一个标记线程移动到一个随机的灰色节点;
• 如果该节点有任何白色的子节点,那标记线程首先会将这样的子节点标记为灰色,然后将该节点标记为黑色;
• 这个过程重复进行直到再也没有灰色节点;
• 所有标记为黑色的对象,都已被证明是可达的,并且必须保持存活状态;
• 白色节点符合收集条件,对应不再可达的对象。

在这里插入图片描述

并发收集经常使用一种叫作原始快照(snapshot at the beginning, SATB)的技术。这意味着如果对象在收集周期开始时是可达的,或者那时已经分配,则收集器会将其视为活的。这就给算法增加了一些小麻烦。比如,当 Mutator4 线程需要创建新对象时,如果此时垃圾收集正在运行,那么这些对象需要标记为黑色,如果没有,则它们需要标记为白色。

三色标记算法还需要多做一些工作,以确保由正在运行的应用程序线程引入的修改不会导致活对象被收集。这是因为在并发收集器中,标记线程在执行三色算法的同时,应用(Mutator)线程正在改变对象图。

考虑这样的情况:一个对象已经被标记线程标记为黑色,然后又被 Mutator 更新,指向了一个白色的对象,如下图所示。

在这里插入图片描述

如果从灰色对象指向新的白色对象的引用现在都被删除了,那就会出现这样一种情况,即这个白色对象应该仍然是可达的,但是它会被删除,因为根据算法的规则,这个对象不会被找到。

这个问题可以通过几种不同的方式来解决。比如我们可以将黑色对象的颜色改回灰色,在Mutator 线程处理更新时,将其加入到需要处理的节点集合中。该方法使用了一个“写屏障”来进行更新,而且它还有一个很好的算法特性,那就是在整个标记周期中能保持三色不变性(tri-color invariant)。

在并发标记期间,任何黑色对象节点都不能持有指向白色对象节点的引用。——三色不变性

另一种方法是使用一个队列,保存所有可能会破坏三色不变性的修改,然后在主阶段结束后运行一个“修正”阶段。不同的收集器可以根据性能或者所需的加锁的数量等标准,用不同方式来解决三色标记的这个问题。

下一节将介绍低延迟的收集器——CMS。虽然 CMS 的适用范围有限,但我们会在其他收集器之前先介绍它。这是因为开发人员往往意识不到,垃圾收集调优需要在多大程度上进行权衡和妥协。

通过先考虑 CMS,可以揭示性能工程师在考虑垃圾收集时应该注意的一些实际问题。希望这样做能让我们更多的是基于证据进行调优,基于固有的一些权衡来选择收集器,而不是按照坊间传说调优。

👬 交友小贴士:

博主GithubGitee同名账号,Follow 一下就可以一起愉快的玩耍了,更多精彩文章请持续关注。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

峰sir~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值