【问题标题】:How do garbage collectors know about references on the stack frame?垃圾收集器如何知道堆栈帧上的引用?
【发布时间】:2012-06-03 11:04:43
【问题描述】:

现代垃圾收集器(如在 CLR、JVM 中)使用哪些技术来判断堆栈中引用了哪些堆对象

具体来说,VM 如何从知道堆栈开始的位置返回到解释所有对堆对象的本地引用?

【问题讨论】:

标签: garbage-collection jvm stack clr


【解决方案1】:

在 Java 中(可能在 CLR 中,尽管我不太了解它的内部结构),字节码是用对象和原始信息输入的。因此,字节码中有数据结构来描述每个堆栈帧中的哪些变量是对象,哪些是原语。当 GC 需要扫描根集时,它使用这些StackMapTables 来区分引用和非引用。

CLR 和 Java 必须有一些类似的机制,因为它们是 exact 收集器。有像boehm collector 这样的保守 收集器,它们将堆栈上的每个偏移量都视为一个可能的指针。他们查看该值(当被视为指针时)是否是堆中的偏移量,如果是,则将其标记为活动。

【讨论】:

  • 收集器如何在字节码中的堆栈位置和本机编译代码中的堆栈位置之间进行映射? (当然要知道,它们可以映射到寄存器以及实际的堆栈位置)
  • .... 实际上,stackoverflow.com/a/12097214/441899 建议它,只是保守地对待堆栈(并且可能是寄存器)。
【解决方案2】:

看看这篇 1996 年 8 月的 Artima 文章,Java's Garbage-Collected Heap;尤其是page 2

任何垃圾收集算法都必须做两件基本的事情。首先,它必须检测垃圾对象。其次,它必须回收垃圾对象使用的堆空间并使其可供程序使用。垃圾检测通常通过定义一组根并确定根的可达性来完成。如果执行程序可以访问对象的根有一些引用路径,则该对象是可访问的。程序始终可以访问根。任何从根可到达的对象都被认为是活动的。无法访问的对象被视为垃圾,因为它们不再影响程序的未来执行过程。

在 JVM 中,根集依赖于实现,但总是在局部变量中包含任何对象引用。在 JVM 中,所有对象都驻留在堆上。局部变量驻留在 Java 堆栈上,每个执行线程都有自己的堆栈。每个局部变量要么是对象引用,要么是原始类型,例如 int、char 或 float。因此,任何 JVM 垃圾收集堆的根都将包括每个线程堆栈上的每个对象引用。根的另一个来源是加载类的常量池中的任何对象引用,例如字符串。加载类的常量池可以引用堆上存储的字符串,如类名、超类名、超接口名、字段名、字段签名、方法名、方法签名等。

根引用的任何对象都是可访问的,因此是活动对象。此外,活动对象引用的任何对象也是可访问的。该程序能够访问任何可访问的对象,因此这些对象必须保留在堆上。任何无法访问的对象都可以被垃圾回收,因为程序无法访问它们。

本文继续探索不同的垃圾收集策略,包括引用计数收集器、跟踪收集器、压缩收集器和复制收集器。


虽然这篇文章很旧,但它仍然适用于今天;并没有太大的改变。不同收集策略的性能有所改进,但没有新的重大进步。

例如,Oracle HotSpot JVM 有一个新的 Garbage-First Garbage Collector,它是一个复制收集器,针对多核处理器和大堆大小进行了性能调整(有关 G1 垃圾收集器的更多信息,请参阅 this answer)。

【讨论】:

  • 太糟糕了,没有一行触及问题的主题。 OP 知道本地引用是根,因此这个问题:GC 如何枚举存储在堆栈上的本地,而不会变得保守和错误,例如一个整数作为参考。
  • @delnan 也许this question 可以更好地回答您的问题,如果不是部分的话。既然提到了 CLR,我不想给出太多以 Java 为中心的答案。
【解决方案3】:

.Net 团队在 CoreCLR 开源后不久发布了有关此主题的有趣文档:Stack Walking

【讨论】:

  • 链接已损坏。
  • 修复了断开的链接
猜你喜欢
  • 2013-12-30
  • 2012-05-29
  • 1970-01-01
  • 2014-03-27
  • 2013-02-20
  • 1970-01-01
  • 2011-01-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多