HotSpot使用的Mark-Compact算法是什么?

2024-04-28

当阅读 Mark-Compact 章节时垃圾收集手册 https://rads.stackoverflow.com/amzn/click/com/1420082795,提出了一系列替代方案,但其中大多数看起来很旧/理论上(例如,2 指压缩和 Lisp2 3 遍方法需要每个对象有一个额外的头字)。

有谁知道 HotSpot 在运行 Mark-Compact 时使用什么算法(我猜是在老一代)?

Thanks


重要免责声明:我不是 GC 专家/作家;下面写的所有内容都可能会发生变化,其中一些可能过于简单。请对此持保留态度。

我只会谈论Shenandoah, as I think我明白;这不是分代 GC。

这里实际上有两个阶段:Mark and Compact。我在这里强烈强调,两者都是同时并且确实会在您的应用程序运行时发生(有一些非常短的 STW 事件)。

现在详细介绍。我已经解释了一些事情here https://stackoverflow.com/a/59120089/1059372,但因为这个答案与另一个不同的问题有关;我将在这里解释更多。我认为遍历活动对象的图对你来说不是什么新闻,毕竟你are读一本关于GC。正如该答案所解释的,当应用程序完全停止(也称为进入安全点)时,识别活动对象很容易。没有人会改变你脚下的任何东西,地板是坚硬的,你可以控制一切。并行收集器就是这样做的。

真正痛苦的方法是并发做事情。 Shenandoah 采用了一种称为Snapshot at the beginning(那本书解释了它 AFAIK),将称之为SATB简而言之。基本上这个算法是这样实现的:“我将开始扫描同时对象图(来自 GC 根),如果有任何变化当我扫描时,我不会更改堆,但会记录这些更改并稍后处理”。

您需要质疑的第一部分是:当我扫描时。这是如何实现的?出色地,beforeconcurrent mark,有一个STW event called Initial Mark。该阶段要做的事情之一是设置并发标记已开始的标志。稍后,在执行代码时,会检查该标志(Shenandoah因此采用了解释器的变化)。在伪代码中:

if(!concurrentMarkingActive) {
    // do whatever you were doing and alter the heap
} else {
    // shenandoah magic
}

在机器代码中可能如下所示:

test %r11, %r11 (test concurrentMarkingActive flag)
jne // concurrent marking is currently active

现在 GC 知道并发标记何时发生。

但是并发标记是如何实现的呢?当堆本身发生变异(不稳定)时,如何扫描堆?你脚下的地板会增加更多的洞,也会消除它们。

这就是“Shenandoah魔法”。对堆的更改会被“拦截”,而不是直接持久化。因此,如果 GC 在此时执行并发标记,并且应用程序代码尝试更改堆,则这些更改将记录在每个线程中SATB queues(开头的快照)。当并发标记结束时,这些队列将被清空(通过STW event called Final Mark)并再次分析那些被耗尽的更改(记住在STW event now).

当这个阶段最终分数结束了 GC 知道什么是活着的,因此什么是隐式垃圾.


接下来是紧凑阶段。Shenandoah现在应该将活动对象移动到不同的区域(以紧凑的方式)并将当前区域标记为我们可以再次分配的区域。当然,在简单的STW phase,这很容易:移动对象,更新指向它的引用。完毕。当你必须这样做的时候同时...

您不能将对象简单地移动到不同的区域并then一一更新您的参考资料。想一想,假设这是我们的第一个状态:

 refA, refB
     |
 ---------
 | i = 0 |
 | j = 0 |
 ---------

该实例有两个引用:refA and refB。我们创建该对象的副本:

refA, refB
     |
 ---------       ---------
 | i = 0 |       | i = 0 |
 | j = 0 |       | j = 0 |
 ---------       ---------

我们创建了一个copy,但尚未更新任何参考资料。我们现在移动单个引用以指向副本:

   refA            refB
     |               |
 ---------       ---------
 | i = 0 |       | i = 0 |
 | j = 0 |       | j = 0 |
 ---------       ---------

现在有趣的部分是:ThreadA does refA.i = 5, while ThreadB does refB.j = 6所以你的状态变成:

   refA            refB
    |                |
 ---------       ---------
 | i = 5 |       | i = 0 |
 | j = 0 |       | j = 6 |
 ---------       ---------

你怎么merge现在这些物体?老实说 - 我不知道这是否可行,而且这也不是一条可行的路线Shenandoah took.

相反,解决方案来自Shenandoah恕我直言,做了一件非常有趣的事情。一个额外的指针添加到每个实例,也称为转发指针:

 refA, refB
      |
 fwdPointer1    
      |         
 ---------       
 | i = 0 |       
 | j = 0 |       
 ---------       

refA and refB指着fwdPointer1, while fwdPointer1到真实的对象。现在让我们创建副本:

 refA, refB
      |
fwdPointer1     fwdPointer2        
      |               |
 ---------       ---------  
 | i = 0 |       | i = 0 | 
 | j = 0 |       | j = 0 | 
 ---------       ---------

现在,我们要切换所有引用(refA and refB) 指向副本。如果你仔细观察,这只需要改变一个指针 -fwdPointer1. Make fwdPointer1指向fwdPointer2你就完成了。这意味着一次更改而不是two(在此设置中)refA and refB。这里更大的好处是您不需要扫描堆并找出指向您的实例的引用。

有没有办法自动更新引用?当然 :AtomicReference(至少在java中)。这里的想法几乎是一样的,我们原子地改变了fwdPointer1 via a CAS(比较和交换),例如:

 refA, refB
      |
fwdPointer1 ---- fwdPointer2        
                     |
 ---------       ---------  
 | i = 0 |       | i = 0 | 
 | j = 0 |       | j = 0 | 
 ---------       ---------

So, refA and refB指向fwdPointer1,它现在指向我们创建的副本。通过单CAS操作,我们已经切换了同时对新创建的副本的所有引用。

然后,GC 可以简单地(同时) 更新所有参考文献refA and refB指向fwdPointer2。最后有这个:

                 refA, refB
                     |
fwdPointer1 ---- fwdPointer2        
                     |
 ---------       ---------  
 | i = 0 |       | i = 0 | 
 | j = 0 |       | j = 0 | 
 ---------       ---------

因此,左侧的对象现在是垃圾:没有引用指向它。

但是,我们需要了解弊端,天下没有免费的午餐。

  • 首先,很明显:Shenandoah添加一个机器头堆中的每个实例(进一步阅读,因为这是错误的;但使理解更容易)。

  • 这些副本中的每一个都会在新区域中生成一个额外的对象,因此在某个时刻,同一对象将至少有两个副本(需要额外的空间)Shenandoah发挥作用,本身)。

  • When ThreadA does refA.i = 5(来自前面的示例),它如何知道是否应该尝试创建副本、写入该副本并CAS that forwarding pointervs 简单地写入对象?请记住,这是同时发生的。与 相同的解决方案concurrentMarkingActive旗帜。有一面旗帜isEvacuationToADifferentRegionActive(不是真实姓名)。如果该标志是true=> Shenandoah Magic,否则只需按原样写入即可。

如果您真正理解最后一点,您自然的问题应该是:

“等一下!这是否意味着谢南多厄做了一个if/else反对isEvacuationToADifferentRegionActive对于每个和单个写入实例 - 是原语还是引用?这是否也意味着每次读取都必须通过forwarding pointer?"

答案以前是 YES;但事情发生了变化:通过这个问题 https://bugs.openjdk.java.net/browse/JDK-8221766(尽管我让它听起来比实际情况要糟糕得多)。现在他们使用Load整个对象的障碍,更多细节here https://developers.redhat.com/blog/2019/06/27/shenandoah-gc-in-jdk-13-part-1-load-reference-barriers/。而不是在每次写入时设置屏障(即if/else反对标志)并通过取消引用forwarding pointer每次阅读,他们都会移动到load barrier。基本上就是这样做的if/else仅当您加载对象时。由于写入它意味着首先读取,因此它们保留了“空间不变性”。显然这更简单、更好、更容易优化。万岁!

请记住forwarding pointer?好吧,它已经不存在了。我还不明白它的全部细节,但它必须做一些有可能使用的事情mark wordfrom space由于添加了负载屏障,因此不再使用。很多更多详细信息请点击这里 https://developers.redhat.com/blog/2019/06/28/shenandoah-gc-in-jdk-13-part-2-eliminating-the-forward-pointer-word/。一旦我了解了它的内部运作原理,我就会更新这篇文章。

G1与什么没有太大不同Shenandoah是的,但问题在于细节。例如Compact在...阶段G1 is a STW事件,总是。G1 is always世代相传——无论你是否愿意(Shenandoah can有点像那样 - 有一个设置可以控制它),等等。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

HotSpot使用的Mark-Compact算法是什么? 的相关文章

  • Jprofile可以连接到docker中运行的JVM

    我是 JProfiler 的新手 我最近遇到了一个问题 我的Java应用程序在docker中运行 这意味着JVM在docker中运行 但我的jprofile安装在主机上 我知道 jprofiler 必须连接到 JVM 那么 jprofile
  • Java 堆被无法访问的对象淹没

    我们的 Java EE 应用程序开始出现一些严重问题 具体来说 应用程序在启动后几分钟内就运行了高达 99 的老年代堆 不会抛出 OOM 但实际上 JVM 没有响应 jstat 显示老年代的大小根本没有减少 没有垃圾收集正在进行 并且kil
  • JVM HotSpot 上的 Java 异常计数器

    我想知道是否可以在不更改应用程序代码的情况下记录 JVM 级别上发生的每个异常 我所说的每个异常是指捕获和未捕获的异常 我想稍后分析这些日志并按异常类型 类 对它们进行分组 并简单地按类型对异常进行计数 我正在使用热点 也许有更明智的理由这
  • C# 获取分配总数

    有没有办法获得分配总数 注意 分配数量 而不是分配的字节数 它可以是当前线程的 也可以是全局的 以更容易的为准 我想检查特定函数分配了多少个对象 虽然我了解 调试 gt 性能分析器 Alt F2 但我希望能够从程序内部以编程方式执行此操作
  • 为什么访问 PerformanceCounter 会导致第 2 代垃圾回收

    当我从 C 应用程序中访问某些 PerformanceCounters 时 我看到一些奇怪的行为 例如 当我访问 Process Private Bytes 时 似乎我得到了很多第 2 代垃圾收集 对于其他进程计数器来说似乎也是如此 下面的
  • 如何查看JVM中JIT编译的代码?

    有什么方法可以查看 JVM 中 JIT 生成的本机代码吗 一般用法 正如其他答案所解释的 您可以使用以下 JVM 选项运行 XX UnlockDiagnosticVMOptions XX PrintAssembly 根据特定方法进行过滤 您
  • C# 中如何实现引用返回?

    既然 C GC 可以移动内存 那么如何实现引用返回呢 下面的代码会导致 未定义的行为 吗 public struct Record public int Hash public VeryLargeStruct Data public cla
  • PyQT4 signal.connect 是否使对象保持活动状态?

    如果我有一个信号并且向该信号注册了一个对象函数 这会使该对象保持活动状态并停止该对象的垃圾收集吗 E g class Signals signal Qt pyqtSignal def init self QObject init self
  • HotSpot使用的Mark-Compact算法是什么?

    当阅读 Mark Compact 章节时垃圾收集手册 https rads stackoverflow com amzn click com 1420082795 提出了一系列替代方案 但其中大多数看起来很旧 理论上 例如 2 指压缩和 L
  • 什么触发了java垃圾收集器

    我对 Java 中垃圾收集的工作原理有点困惑 我知道当不再有对某个对象的实时引用时 该对象就有资格进行垃圾回收 但是如果它有对实时对象的引用怎么办 可以说我有一个节点集合 它们再次引用更多节点 List 1 gt Node a gt Nod
  • C# 和 .Net 垃圾收集器性能

    我正在尝试用 C 和 NET 制作游戏 并且计划实现更新游戏世界中游戏对象的消息 这些消息将是 C 引用对象 我想要这种方法 因为如果我希望游戏是多人游戏 那么通过网络发送它们会更容易 但是如果我有很多消息 对于垃圾收集器来说不是压力很大吗
  • 这个作用域/闭包什么时候在 javaScript 中被垃圾回收?

    我正在做一门课程 该课程正在讨论范围 闭包并简要提到垃圾收集 课程中提出一个问题 范围保持多久 答案是 直到 不再有任何提及它 是的 所以我们基本上说的是 是的 闭包有点像对隐藏范围对象的引用 所以只要有一些函数仍然有一个闭包 范围 该范围
  • Spark:shuffle操作导致GC长时间暂停

    我在跑Spark 2我正在尝试洗牌大约 5 TB 的 json 我在洗牌期间遇到了很长的垃圾收集暂停Dataset val operations spark read json inPath as MyClass operations re
  • Intellij Idea 使用什么 JVM 来启动?

    我是 Eclipse 用户 最近决定尝试 Intellij Idea 我的操作系统是 Ubuntu 12 使用 Eclipse 时 可以通过在 eclipse ini 中指定来轻松选择用于启动 Eclipse 的 JVM http wiki
  • 在 HP Load Runner 的 VuGen 中加载 javai.dll 时出现错误

    当我尝试在 HP load runner 的 VuGen 中编译一个简单的脚本时 无法启动 JVM 并出现以下错误 Java VM Internal Error Getting Error Loading javai dll 我在用着 HP
  • 一旦组件被销毁,在 initComponent 函数中创建的存储是否会发生内存泄漏,或者这些存储是否会被垃圾收集?

    这是从另一个问题中衍生出来的一个问题 在 ExtJS 4 中具有相同视图并多次存储的最佳实践 https stackoverflow com questions 27333787 best practice to have the same
  • 如何判断我是在 64 位 JVM 还是 32 位 JVM 中运行(在程序内)?

    如何判断应用程序运行的 JVM 是 32 位还是 64 位 具体来说 我可以使用哪些函数或属性来在程序中检测到这一点 对于某些版本的 Java 您可以使用标志从命令行检查 JVM 的位数 d32 and d64 java help d32
  • 为什么 MetaSpace 大小是已用 MetaSpace 的两倍?

    我写了一个程序来模拟MetaSpace OOM 但我发现MetaSpace Size几乎总是两倍大Used MetaSpace Why 我用标志运行我的程序 XX MaxMetaspaceSize 50m 程序抛出OOM时Used Meta
  • V8 如何管理它的堆?

    我知道V8的垃圾收集在工作时 会从GC的root开始追踪 这样无法到达的对象就会被标记然后被清除 我的问题是GC是如何遍历那些对象的 必须有一个数据结构来存储所有可达或不可达的对象 位图 链接表 顺便说一句 JVM 也做同样的事情吗 艾伦秀
  • 监控 Java 应用程序上的锁争用

    我正在尝试创建一个小基准 在 Groovy 中 以显示几个同步方法上的高线程争用 当监控自愿上下文切换时 应该会出现高争用 在 Linux 中 这可以通过 pidstat 来实现 程序如下 class Res private int n s

随机推荐

  • Jersey 2 - ContainerRequestFilter get 方法注解

    我试图获取 ContainerRequestFilter 对象中的方法注释 控制器 GET RolesAllowed ADMIN public String message return Hello rest12 容器请求过滤器 Provi
  • 如何更改Android中开关的文本颜色

    我正在创建一个使用 Android 4 0 的应用程序 我想知道是否可以更改开关中文本的文本颜色 我尝试过设置文本颜色 但不起作用 有任何想法吗 提前致谢 你必须使用android switchTextAppearance属性 例如 and
  • 为什么即使优化级别为 3,向量分配也需要花费这么多时间?

    以前我在这里问过类似的问题 Android NDK vector resize 太慢 与分配有关 https stackoverflow com q 58745415 5709159 问题是这段代码 include
  • java中的内联初始化块

    我有课 public class MyMain public static void main String arg Temp t new Temp System out println instance initialize class
  • 加载 XSLT 文件时解析相对路径

    我需要使用 Apache FOP 进行 XSL 转换 我的代码如下 Setup FOP Fop fop fopFactory newFop MimeConstants MIME PDF out Setup Transformer Sourc
  • 在视图之间传递变量 SwiftUI

    再次基本问题 我想让变量 anytext 对于我要添加的所有未来视图都可见且可访问 在我的例子中 变量将是String 如果是的话 程序会改变吗 Float 我怎样才能将其另存为全局变量 如果我重新启动应用程序 变量会自行删除吗 如何保存即
  • 一起使用similar_text和strpos

    我想创建一个简单的搜索引擎 在用户输入中查找关键字 我知道我可以使用 strpos 来检查字符串中是否存在单词 但是 我希望用户能够拼写错误的单词 例如 userInput What year did George Washingtin b
  • 使用 IcyStreamMeta 从 SHOUTcast 获取元数据

    我正在为 Android 编写一个应用程序 从 SHOUTcast mp3 流中获取元数据 我正在使用我在网上找到的一个非常漂亮的类 我稍微修改了一下 但我仍然有两个问题 1 我必须使用 TimerTask 不断 ping 服务器来更新元数
  • Keras TimeDistributed Conv1D 错误

    这是我的代码 cnn input Input shape cnn max length emb output Embedding num chars 1 output dim 32 input length cnn max length t
  • 如何为启动的 setup.exe 创建日志文件

    我继承了一些InstallShield InstallScript项目 我目前正在使用InstallShield 2009 当我运行 setup exe 时 我似乎无法创建日志文件 我需要指定哪些命令行选项 InstallShield 有一
  • Makefile 头依赖项

    我是使用 make 的新手 并且一直在通过以下方式学习基础知识本教程 http www cs colby edu maxwell courses tutorials maketutor 这是本教程中的最后一个 makefile 示例 IDI
  • 使用 6.0 API (Android) 从服务器发送和接收数据

    我真的很困惑 我正在尝试开发一个简单的功能 允许我从服务器发送和接收数据 操作如下 在一个活动中 我对服务器上的 PHP 文件执行 HTTP POST PHP 文件 获取我发送的数据 通常是字符串 并使用通过 http 发送的参数执行查询
  • Eclipse 构建 Android 应用程序:如何在编译时创建两个版本?

    我正在编写一个 Android 应用程序 并希望基于相同的代码创建两个版本 免费版本和高级版本 我有两个版本的一个代码库 具有各种运行时检查来启用或禁用某些功能 例如 public class MyAppContext extends Ap
  • 使用 Retrofit 的 Google 地图方向 API

    我想绘制两个位置之间的路线 我使用retrofit库来调用API 我没有得到任何回应 我需要 ArrayList 中的折线 我怎么做到这一点 还需要帮助来创建 GsonAdapter 谢谢 在活动中 String base url http
  • Firebase 无需身份验证即可初始化 - firebase.auth 不是函数

    我无法弄清楚为什么我的一个 Firebase 应用程序使用 auth 进行初始化 而另一个则没有 我在这里遵循了节点安装选项 https firebase google com docs web setup https firebase g
  • Laravel Passport 中间件保护路由“未经身份验证”问题

    我使用 Laravel Passport 进行身份验证 因此我将路由置于中间件保护中 UPDATED 为了清楚起见 我也添加了 UsersController public function getUser users Auth user
  • Ant 复制文件而不覆盖

    Is there any command in ant to copy files from one folder structure to another without checking the last modified date t
  • 如何从默认标签栏删除底部

    我需要添加一个没有应用栏的选项卡栏 我从 StackOverflow 获得了一个使用灵活空间的解决方案 它正在工作 但它在选项卡栏底部产生了额外的不需要的空间如何删除或隐藏它 我的完整代码 import package flutter ma
  • 尝试在 Angular 项目中导入 date-fns 时,Jest 测试失败

    我最近将我的一个 Angular 项目更新到 Angular 13 更新后 我在尝试在项目中运行单元测试时遇到了一些奇怪的错误 我在一个新的 Angular 项目中创建了一个最小的示例来重现此行为 import format from da
  • HotSpot使用的Mark-Compact算法是什么?

    当阅读 Mark Compact 章节时垃圾收集手册 https rads stackoverflow com amzn click com 1420082795 提出了一系列替代方案 但其中大多数看起来很旧 理论上 例如 2 指压缩和 L