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算法是什么? 的相关文章

  • C++ 中的垃圾收集——为什么?

    我不断听到人们抱怨 C 没有垃圾回收功能 我还听说 C 标准委员会正在考虑将其添加到该语言中 恐怕我只是不明白它的意义 使用 RAII 和智能指针消除了它的需要 对吗 我唯一的垃圾收集经验是在几台廉价的八十年代家用计算机上 这意味着系统会时
  • 将 Kotlin .kt 类打包到 JAR 中

    我如何构建HelloWorld kt as a JAR以便它运行 thufir dur kotlin thufir dur kotlin kotlinc HelloWorld kt include runtime d HelloWorld
  • JVM锯齿状空闲进程

    我目前正在进行一项涉及 JVM 及其内存使用工作原理的研究 我不明白的是 JVM在空闲时用什么填充它的内存 只是为了在堆几乎达到时释放它 为什么使用的内存不只有一条平线 顺便说一句 这个 java 应用程序托管在 glassfish 上 但
  • 如何制作.Net或JVM语言?

    我看到了 NET 和 JVM 的所有这些新语言 一个人如何开始制作一个 我找不到关于 JVM 或 MSIL 规范的任何好的文档 Edit 我已经知道如何解析 我更感兴趣的是如何有这么多人基于这些平台创建新语言 你有点幸运 为 NET 开发的
  • 未分配给变量的类实例是否会过早被垃圾回收?

    我什至不知道我的问题是否有意义 这只是我不明白的事情 并且在我的脑海中旋转了一段时间 考虑有以下课程 public class MyClass private int myVar public void DoSomething Do som
  • 如果Java的垃圾收集器移动对象,那么Object.hashCode和System.identityHashCode是什么?

    我经常听说这些方法 Object hashCode and System identityHashCode 返回对象的地址 或从地址快速计算出的内容 但我也很确定垃圾收集器会移动并压缩对象 由于哈希码无法更改 因此这就出现了问题 我知道这不
  • c++/cli 终结器中免费 GCHandle 的最佳实践

    我在 c 中有一些函数 我会在 net 应用程序中使用它 为此 我用 C cli 编写了一个包装器类 C 接口中有一个回调函数 并将其包装在 net 委托中 但是我应该如何释放回调 gcHandle 的非托管资源呢 是否允许在终结器中从 G
  • javascript 中可以预防失效的监听器吗?

    我的问题确实是 是流失的听众问题 http wikipedia org wiki Lapsed listener problem在 javascript 中可以预防吗 但显然 问题 这个词会导致问题 维基百科页面说 流失的听众问题可以通过主
  • 在 Android 上运行 Java 字节码 - DalvikVM 之上的 Sun JVM

    由于 java 实现 OpenJDK 和Android的虚拟机DalvikVM是开源的 因此必须可以在Google的DalvikVM之上实现Sun的JavaVM 这将使运行基于 JVM 的应用程序和语言成为可能 Clojure Jython
  • 从 Java 内部限制 CPU

    我在这个 和其他 论坛中看到了许多具有相同标题的问题 但似乎没有一个问题能完全解决我的问题 就是这个 我有一个 JVM 它占用了托管它的机器上的所有 CPU 我想限制它 但是我不能依赖任何限制工具 技术external到 Java 因为我无
  • Perl 中如何释放内存?

    我的代码如下所示 my var my var new while 1 while my k v each var a sub v var A map var var new keys var new var new B sub a sub
  • 将值存储为变量或再次调用方法更好吗?

    最近 我开始学习一些Java 从我对 JVM 的了解来看 JIT 使其在需要 CPU 周期的操作 即调用方法 上变得非常快 但也使其对内存产生了饥饿感 因此 当我需要与以前相同的方法获得相同的输出时 将之前的输出存储在变量中并再次使用它 同
  • Java 8:为什么元空间大小增加但加载的类数量保持不变?

    在 Jdk 8 上运行的应用程序中 我们使用 VisualVM 来跟踪加载的类的使用情况和元空间的使用情况 在我们的应用程序运行时的某个时间点 我们看到加载的类的数量不再增加 但元空间的大小在程序运行时仍然增加 那么除了类之外 还有什么东西
  • 如何使用 JAVA_OPTS 环境变量?

    我该如何使用JAVA OPTS变量来配置Web服务器 Linux服务器 我该如何设置 Djava awt headless true using JAVA OPTS JAVA OPTS是一些服务器和其他 Java 应用程序附加到执行调用的标
  • 即使在 GC Collect 和 WaitForPendingFinalizers 之后,窗口对象在关闭后也未释放?

    这是一个简单的测试应用程序 可帮助了解 WPF 内存使用情况 我想了解的关键是为什么MainWindow即使在关闭并等待 GC 完成之后 仍然被引用并且它的内存没有被释放 参见下面的代码清单 文本 MainWindow Finalizer
  • Java 表达式树 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 是否有相当于 net的 LINQ 下的表达式树JVM 我想实现一些类似 LINQ 的代码结构Scala
  • 如何正确取消引用然后删除 JavaScript 对象?

    我想知道从内存中完全取消引用 JavaScript 对象的正确方法 确保删除时不会在内存中悬空 并且垃圾收集器会删除该对象 当我看这个问题时在 JavaScript 中删除对象 https stackoverflow com questio
  • Java:如何像 C++ 一样存储和检索内存地址

    我有 C 背景 在 C 中 我可以存储我刚刚在全局数组中新建的内存地址 并在以后重新使用它 例如 假设我有两个类 X Y 并且我创建了两个对象 x y 全局数组 StoreAddresses 2 定义为 uint32 t StoreAddr
  • 附加到已经运行的 JVM

    有没有办法附加到已经运行的 JVM 例如 在 JNI 中您可以使用JNI CreateJavaVM创建一个虚拟机并运行一个 jar 并检查它的所有类 但是 如果 jar 已经在运行 我找不到附加到其 JVM 并与其类通信或获取其的方法env
  • PowerShell v4 中的本机内存泄漏?

    我安装的 PS v4 似乎存在泄漏 CLR版本 4 0 30319 34209构建版本 6 3 9600 17400 我运行以下命令 内存快速无限增长 while true New Object System Object 如果我跳出循环并

随机推荐

  • 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