可以用 C++ 原始指针实现 GC 吗?

Posted

技术标签:

【中文标题】可以用 C++ 原始指针实现 GC 吗?【英文标题】:can a GC be implemented with C++ raw pointers? 【发布时间】:2009-06-28 01:09:38 【问题描述】:

我想知道如何使用 C++ 完整的指针算术功能来实现垃圾收集器。此外,在 Java 等语言中,我无法将文字地址分配给引用。在 C++ 中,它非常灵活。

我相信 C# 两者都有,但同样,C# 中的不安全指针是程序员的责任。

EITD :: 伙计们,我在问“目前的”C++ 指针是否可以在理论上被 GC 处理?

【问题讨论】:

是“它们目前的情况”定义为“它们如何根据标准”,还是“它们如何根据实际编译器”?编译器不会添加 GC 工作所需的元数据,但标准中并没有禁止它。 啊哈,这是关于标准的。我不知道如何提出这个问题:) C++ 通过智能指针进行确定性垃圾收集(如果使用正确)。不幸的是,就像 C++ 中的所有内容一样,它们很容易被没有经验的人滥用。 【参考方案1】:

指针算术不是根本问题。 GC 必须始终处理重新分配的指针,而指针算法只是另一个例子。 (当然,如果允许指向不同缓冲区的指针之间的指针算术,则会导致问题,但事实并非如此。您可以对指向数组 A 的指针执行的唯一算术是重新定位它的算术在那个数组中。

真正的问题是缺少元数据。 GC 必须知道什么是指针,什么不是。

如果遇到0x27a2c230这个值,它必须能够判断是不是

一个指针(在这种情况下,它必须跟随指针以递归方式将目标标记为“正在使用”) 一个整数(相同的值是一个完全有效的整数。也许它根本就不是一个指针) 或者别的什么,比如一个字符串。

它还必须能够确定结构的范围。假设值 一个指针,并且它指向另一个结构,GC 必须能够确定该结构的大小和范围,因此它知道应该扫描哪个地址范围以获取更多指针.

GC'ed 语言有很多基础设施来处理这个问题。 C++ 没有。

Boehm 的 GC 是您通常可以获得的最接近的 GC,它是保守的,如果某物 可能 是指针,GC 会假定它是指针,这意味着一些数据不必要地保持活动状态.因此,它可能会保留应该被 GC 处理的数据。

当然,原则上所有这些基础架构都可以添加到 C++ 编译器中。标准中没有规定不允许它存在。问题是它会严重影响性能并消除很多优化机会。

【讨论】:

【参考方案2】:

是的。早在 1990 年代,NeXT 就有一个系统是这样工作的:他们跟踪每个已分配的 C/C++ 内存块。他们定期扫描内存以查看是否存在与该内存地址关联的 4 字节(或 8 字节)值。如果不是,他们假设没有引用,他们释放内存。这很简单!但有时它会搞砸。

这里有一些其他的方法: http://www.hpl.hp.com/personal/Hans_Boehm/gc/

http://developers.sun.com/solaris/articles/libgc.html

【讨论】:

【参考方案3】:

你的意思是像smart_ptr这样的东西吗?

【讨论】:

【参考方案4】:

Boehm GC?

它是用 C 而不是 C++ 编写的,并且不是 C++ 的完美匹配,因为它不调用析构函数,但可能或多或少符合要求。

【讨论】:

【参考方案5】:

当然,为什么不呢?从垃圾收集器的角度来看,指针算法与索引到数组没有什么不同。像 xor 双向链表这样的东西搞砸了 GC,但这不是指针算术,此外还有未定义的行为。此外,Boehm 存在,这是一种经验证据,它是一个适用于 C++ 的垃圾收集器。有一段时间,他们威胁要将 GC 作为 C++0x 的可选部分。

【讨论】:

【参考方案6】:

问题是你为什么要这样做?在 90% 的情况下,您认为垃圾回收会很好,智能指针向量(如在删除对象时删除对象的向量/映射中)就足够了。其余 10% 中的大部分都可以通过使用 ref 计数接口来处理。

【讨论】:

这只是一个让我困惑的理论问题:)【参考方案7】:

C++ 指针算法允许您构造 N+1 个指向 T[N]、&T[0]...&T[N-1] 和标记 &T[N-1]+1 的指针。这些都是指向 T[N] 数组对象的指针。从这个意义上说,它们类似于其他“内部”指针,例如 &foo.bar(对象成员的地址)。

其他指针算术是未定义的行为,UB 的一个明显示例可能是 GC 无意中删除了数组。

【讨论】:

【参考方案8】:

是的,GC 可以独立于类型安全来实现,而 C++ 在很大程度上没有;但是 GC 可用的优化机会将受到语言允许的类型余量的限制。

如果一个人愿意接受运行时检查的指针操作,从而在语言放弃的地方重新实现类型安全,那么 GC 可以更具侵略性。事实上,垃圾收集 C++ 通常仅限于保守的收集器,例如 Boehm-Demers-Weiser GC。

【讨论】:

保守集合依赖于所有有效内存至少有一个有效指针。如果你模糊了一个引用,被引用的内存可能会被释放;但是如果一个随机词指向某个内存,它就会被视为一个引用并保持它的存在。 参考文献是如何维护的?如果它类似于 smart_ptr 那么指针不是原始的。 保守的收集器可以检查有效内存的每个指针大小的字,并检查它是否“看起来像”指向有效分配块的指针,例如通过类似于页表的机制 - 隐藏在指针值之外的前缀树。对于更激进的精确收集器,至少需要有关指针的类型信息以及指针操作和算术的运行时检查,以获得所需的类型安全级别。 但是类型信息可以由与垃圾收集器协调设计的编译器提供;就像用于指针算术类型检查的 RTL 存根一样。【参考方案9】:

我不是 GC 实现方面的专家,但如果您允许使用指针算法,那么您永远无法确定内存中的给定地址不能再被引用。

举个例子

int *crazy_pointer;
srand ( time(NULL) );

while(true) 
  crazy_pointer = (int *) rand() % MAX_SIZE_OF_MEMORY:
  printf("$d",*crazy_pointer);

实际上,我以一种非常基本的方式使用了指针算法来打印内存中随机位置的内容。这意味着机器上的所有内存基本上都是可访问的。如果它是可达的,它就不能被 GCed。

是的,以上内容可能会导致现代操作系统崩溃,但此代码是完全合法的 C/C++。如果您允许指针算术,您将无法实现可以防止这种滥用的 GC :)

【讨论】:

保守 GC 可以很好地处理指向随机位置的指针 - 它依赖于操作系统为其提供有效内存页面列表,并且根本不会跟随指向未映射页面的指针。 好的,它不能进入​​未映射的页面,这样可以防止崩溃,但是它怎么知道我不能再引用一段内存呢?我仍然可以在运行时为 crazy_pointer 分配任意内存位置,而 GC 永远不会知道我选择了什么。 简单。通过说“你正在做的是未定义的行为”。该语言不允许您获取 rand() 的结果并将其用作指针。它不是“完全合法的 C/C++”。根据标准,指针算术比许多人想象的要有限得多。 :) 你是在告诉我那个代码,不会编译吗?它会。 GC 将如何处理上述代码。 仅仅因为某些东西可以编译并不意味着它是有效的。您的代码是完全无效的代码,C/C++ 甚至禁止指向应用程序分配内存之外的内存。 (和过去)

以上是关于可以用 C++ 原始指针实现 GC 吗?的主要内容,如果未能解决你的问题,请参考以下文章

c++实现多态一定要用指针吗

可以将任何原始类型传递给需要指针的函数吗?

C++ 共享指针四宗罪

C++ 共享指针四宗罪

C++ 共享指针四宗罪

C# 中是不是有像 C++ 这样的指针?安全吗?