1

我知道如何找到根,但问题是,(AFAIK)必须在运行时找到它们。为此,您需要一个可能溢出的固定大小的容器或一个可调整大小的容器。我不想使用固定大小的容器,因为很难判断要保留多少空间(而且可能很浪费)。可调整大小的容器似乎最好,但问题是,GC 在没有足够空间时运行,因此可调整大小的容器将无法存储它需要的内容。那么有了这些条件,GC 根是如何存储的呢?

4

1 回答 1

1

GC 根是堆外的一个位置,可以包含对堆内对象的引用。位置可以是任何可以存储引用的东西。通常它是存储 32 或 64 位地址的 4 或 8 字节内存,但也可以是机器寄存器或磁盘空间。有时一个位置被称为“插槽”,因为您可以“插入”一个引用。经典的标记和扫描收集器首先标记根引用的所有对象,然后从那里继续跟踪。

根存储在哪里以及如何存储取决于 VM,当您考虑部分 GC、线程和 JITting 等问题时​​,它会变得非常复杂。但从概念上讲,它很简单。假设你有一种类似 Python 的语言,只有函数和全局变量以及以下代码:

 0: FOO = "hel"
 1: BAR = "hi"
 2: def foo(x):
 3:    y = x + "there"
 4:    <GC HERE>
 5:    return
 6: def bar(x):
 7:    y = x + "lo"
 8:    foo(y)
 9:    return
10: bar(FOO)
11: ...

假设 GC 发生在指示的行上。调用堆栈看起来像这样:

Return address to line 11
Reference to object "hello"
Return address to line 9
Reference to object "hellothere"

GC 将扫描此调用堆栈,以区分返回地址和引用,并标记它找到的对象。然后它会对全局引用做同样的事情。它们可以存储在堆上的字典(哈希图)中并由单个根引用:

{name("FOO") : "hel", name("BAR") : "hi"}

请注意,存储所有根所需的空间很小。全局变量只需要 8 个字节(一个引用),调用堆栈上的每个元素只需要 8 个字节。您可能会用完堆栈空间并导致堆栈溢出,但是如果为堆栈预分配了 256kb 并进行了适当的尾调用优化,这不是问题。

于 2020-06-02T13:26:54.720 回答