解释Azul的“无动作”垃圾收集器

时间:2010-12-20 15:50:53

标签: garbage-collection

我刚看过这个:

http://www.artima.com/lejava/articles/azul_pauseless_gc.html

虽然我对编译器有一些经验,但我没有做任何与垃圾收集有关的事情;对我来说是一个很大的黑盒子。

我很难理解文章中提到的问题。我理解这个问题(执行大多数垃圾收集器时有一个暂停),我知道他们声称他们的实现没有那个问题。但是我不明白为什么/如何在第一时间发生这个问题(似乎在原始文本中可以理解这一点),因此我不明白为什么他们的解决方案可能有效。

有人可以向我解释一下:

  1. 为什么垃圾收集者通常会暂停
  2. 为什么Azul的gc没有那个问题?
  3. 当以图形方式解释时,我倾向于更好地理解这种事情 - 可能用代码编辑器完成的小内存模式就足够了。

    谢谢!

4 个答案:

答案 0 :(得分:47)

他们谈论压缩堆时不可避免的暂停。你会看到,当你分配和释放大量不同大小的对象时,你就会对堆进行分段(就像你分割你的硬盘一样)。当碎片变得过于极端时,你必须通过保留大量内存来清理/碎片整理/压缩堆,将所有对象移动到那里(没有任何碎片)并使用它们以前的位置作为新的内存块而不包含任何对象,即没有碎片。

执行此操作时,您将对所有移动的对象的所有引用无效。要防止这种情况,必须防止使用引用预压缩对象位置的引用。到目前为止最简单的方法是暂停整个应用程序,移动对象,然后更新所有引用。当然,这可能会产生很大的开销。

因此,Azul提出的解决方案是这样的:它们建立了一个“读屏障”,允许GC拦截解除引用,这样他们就可以懒惰地更新实际使用的引用。

答案 1 :(得分:15)

  

为什么垃圾收集者一般会暂停?

     
    

GC通过跟踪从一组全局根开始的可到达堆块(全局变量,     线程堆栈和CPU寄存器)。 GCs位于从快照到动态的滑动范围内。     快照GC可以从全局根和堆拓扑的快照开始工作。即时通讯     当mutator运行时,逐步更新它们对堆的解释。

  

跟踪不是"为什么垃圾收集者一般会暂停#34;有更大的理由暂停:对象重定位是占主导地位的。

但是到了动态与快照跟踪器及其相对效率的关键点:即时跟踪可以更快速地进行快照跟踪器。您在描述[VCGC]时引用的同一篇论文将Azul的先前非代Pauseless收集器归类为精确的波前[3]示踪剂:

" ...大多数实际的收藏家使用波前的保守抽象而不是这里提供的精确定义。也就是说,以对象粒度跟踪波前。然而,精确的波前不仅仅是理论上的,并且最近已经在Azul Java服务器的硬件辅助收集器中使用,它在每个指针[2]中都有一个“未标记”位。"

Azul的C4具有这种品质,但使用纯软件,自我修复的LVB读取障碍来实现它。

  
    

完全快照GC获得高吞吐量,因为收集器几乎完全运行     独立于mutator但具有高延迟,因为拍摄快照会导致暂停。     完全即时的GC可以实现低延迟,因为一切都是逐步完成但却很低     吞吐量,因为mutator和GC之间的细粒度通信。

  

一个精确的波前跟踪器同样有效(来自"不花时间在不需要的对象上"论文中讨论的观点)作为一个世界上的跟踪器,根据定义,它也有一个说明波前。与基于快照的方法相比,精确的波前扫描不会以任何方式降低吞吐量,或者需要收集器和增变器之间的任何更多通信。它具有良好或更好的吞吐量,因为它的精确性确保它永远不必重复任何跟踪工作。

  

为什么Azul的gc没有这个问题?

     
    

他们仍然遇到这个问题,但他们通过在硬件中实现读屏障来减轻它。     之前曾提出过读取障碍,但软件读取障碍会使吞吐量降低太多     因为指针读取比写入更常见。

  

如上所述,如果"问题"由于动态与快照行为导致效率低下,然后C4没有它,因为它是一个精确的波前跟踪器。此外,Azul的C4收集器不需要或使用硬件读取障碍,因为它在vanilla x86和Linux系统上运行,并且在基于快照的跟踪收集器所做的硬件上实现了更好的吞吐量(参见吞吐量比较) [1])...

然而,"问题"在这个问题中被称为"为什么垃圾收集者一般会暂停?"波前精确度(或不是)在垃圾收集器中的主要暂停几乎没有。并存和大部分并发(即使效率低于C4)标记确实存在,但它们的收集器仍然暂停。问题是跟踪只是收集的一部分。追踪只会告诉你什么是活着的以及它在哪里。它没有给你任何回忆,它肯定不会破坏碎片化的内存。该主题在各种学术论文中进行了深入讨论(参见C4论文[1]的大量参考文献)。

压缩(以及隐含的对象重定位)似乎是当前在服务器JVM上运送收集器的致命弱点,以及固有地导致它们进行[大]暂停的事情。将一个对象从一个地方重新定位到另一个地方的简单操作意味着在程序使用它们之前必须修复指向该对象的所有引用。对于大多数商业运输收集器,这意味着停止世界暂停,使应用程序在修复引用时不运行。

C4利用自我修复的LVB屏障([2]中引入的一种新型读屏障,并在[1]中以大量使用的形式大量使用),以避免在允许应用程序运行之前修复引用。这就是它如何避免其他收藏家最终不得不采取的暂停。与以前的非自愈障碍相比,自我修复质量将读取屏障的动态成本降低了几个数量级(例如学术工作中其他并行压实机中使用的小溪式屏障,以及某些实际的时间收集者)。这种显着降低的读屏障成本的结果是它可用于分代收集和服务器级JVM。

[1]:" C4:连续并发压缩收集器" http://dl.acm.org/citation.cfm?id=1993491&dl=ACM&coll=DL&CFID=85063603&CFTOKEN=84074207 [2]:" Pauseless GC算法" http://static.usenix.org/events/vee05/full_papers/p46-click.pdf [3]:"正确性 - 保持并发垃圾收集算法的推导" www.srl.inf.ethz.ch/papers/pldi06-cgc.pdf

(Azul Systems的EMEA技术经理Graham Thomas)

答案 2 :(得分:8)

  

为什么垃圾收集者一般会暂停?

GC通过跟踪从一组全局根(全局变量,线程堆栈和CPU寄存器)开始的可到达堆块来工作。 GCs位于从快照到动态的滑动范围内。快照GC可以从全局根和堆拓扑的快照开始工作。当变换器运行时,即时GC会逐步更新对堆的解释。

全快照GC获得高吞吐量,因为收集器几乎完全独立于mutator运行但具有高延迟,因为拍摄快照会导致暂停。由于mutator和GC之间的细粒度通信,所以一切即时的GC都可以实现低延迟,因为一切都是以递增的方式完成的,但吞吐量很低。

在实践中,所有地方选区都介于这两个极端之间。 VCGC主要是快照GC,但它使用写屏障来保持收集器了解堆拓扑的更改。 Staccato是世界上第一个并行,并发和实时GC,但它仍然会批量处理某些操作,以保持堆栈分配的效率。

  

为什么Azul的gc没有那个问题?

他们仍然遇到这个问题,但他们通过在硬件中实现读屏障来减轻它。之前曾提出过读取障碍,但软件读取障碍会使吞吐量降低太多,因为指针读取比写入更常见。

答案 3 :(得分:1)

为什么垃圾收集器不仅仅是mprotect(region_it's_working_on, PROT_READ)并且实现了更新所有被访问对象指针的SIGSEGV处理程序?是的,你必须跟踪对象的所有指针。