【问题标题】:Shenandoah self healing barriers雪兰多自愈障碍
【发布时间】:2020-09-29 17:27:59
【问题描述】:

标题几乎说明了一切 - 这些自我修复障碍是什么?为什么它们在 Shenandoah 2.0 中很重要?

【问题讨论】:

    标签: java garbage-collection jvm shenandoah


    【解决方案1】:

    这个解释将附在the first partthe second part 我试图提出的一些答案Shenandoah 2.0

    要真正回答这个问题,我们需要了解load reference barrier 的实现方式以及GC cycle 的一般行为方式。

    当某个GC cycle被触发时,首先选择垃圾最多的regions;即:集合集中的对象非常少(这在未来很重要)。 理解这个主题的最简单方法是通过一个例子。假设这是一个现在存在于某个区域的方案:

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

    该区域中存在一个对象,并且有两个引用指向它:refArefBGC 启动并选择该区域进行垃圾收集。 同时应用程序中有活动线程尝试通过refArefB 访问此对象。由于此对象在某些时候是alive,因此需要将其撤离到一个新区域(mark-compact 阶段的一部分)。

    所以:GC活动的,同时,我们通过refA/refB读取。当我们做这个阅读时,我们踩到了load-reference-barrier,实现了here。注意它内部有一些“过滤器”(通过一堆if/else 语句)。具体来说:

    • 它检查“疏散当前是否正在进行”。这是通过在疏散首次开始时设置的线程本地标志来完成的。让我们假设答案是:是的。

    • 它检查我们当前正在操作的对象是否在“集合集”中。这意味着它当前被标记为活动的。让我们假设这也是“是”。

    • 最后的检查是确定这个对象是否已经“复制”到不同的区域(它被疏散了)。假设答案是“否”,即:obj == fwd

    此时,发生了一些事情。首先创建一个副本并mark becomes forwardee

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

    只有在代码的后面,refArefB 才会更新为指向 new(复制的)对象。但这意味着一件有趣的事情。这意味着直到refArefB 实际上指向新对象,它们当前指向的对象位于“集合集”中。所以,如果 GC 处于活动状态,即使 forwardee 已经建立,load-reference-barrier 仍然需要做一些工作。

    所以Shenandoah 背后的非常 聪明人说:为什么不在forwardee 建立后立即更新那里的引用(或者当forwardee 已经为其他人所知时)参考)? And this is exactly what they did.

    假设我们回到最初的绘图:

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

    我们再次“启用”所有过滤器:

    • 有一个线程通过refA读取

    • GC 处于活动状态

    • refArefB 后面的对象是活动的。

    这就是“自我修复障碍”会发生的事情:

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

    区别很明显:refA 被当场通过CAS 移动到指向新对象。如果要通过refA 再次读取再次(GC 仍然处于活动状态),这将导致更快的加载引用屏障执行。为什么?因为refA 指向的对象不是在“集合集”中。

    但这也意味着,如果我们通过refB 读取并看到fwd != obj - 代码可以执行相同的技巧并更新refB,当时第一次读取是通过refB 发生的。

    据知情人士称,这提高了性能,我相信他们。

    【讨论】:

      猜你喜欢
      • 2021-09-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-15
      相关资源
      最近更新 更多