C++原始指针能否实现垃圾回收(GC)?

6
我想知道如何使用C++指针算术的全部功能实现垃圾收集器。此外,在像Java这样的语言中,我无法将字面地址分配给引用。在C++中非常灵活。
我相信C#都有,但是C#中的不安全指针是程序员的责任。
EITD :: 各位,我想问一下理论上是否可以对当前的C++指针进行垃圾回收?

“它们当前的定义是按照标准还是实际编译器的定义?”编译器不会添加GC所需的元数据,但标准中也没有禁止这样做。 - jalf
啊哈,那是关于标准的。我不知道如何提出问题 :) - Khaled Alshaya
C++通过智能指针实现了确定性垃圾回收(如果使用正确)。不幸的是,就像C++中的所有东西一样,它们容易被缺乏经验的人滥用。 - Martin York
9个回答

8
指针算术并不是根本问题。垃圾回收器必须处理指针被不断重新分配的情况,而指针算术只是其中的一个例子。(当然,如果允许在指向不同缓冲区的指针之间进行指针算术,那么会引起问题,但实际上并没有这样做。您可以对指向数组A中的指针执行的唯一算术操作是将其重新定位在该数组内部。)
真正的问题是缺乏元数据。垃圾回收器必须知道什么是指针,什么不是。
如果遇到值0x27a2c230,它必须能够确定它是
- 指针(在这种情况下,它必须跟随指针递归地将目标标记为“正在使用”) - 整数(相同的值是完全有效的整数。也许根本不是指针) - 或者其他东西,比如字符串的一部分。
它还必须能够确定结构体的范围。假设该值是一个指针,并且它指向另一个结构体,则垃圾回收器必须能够确定该结构体的大小和范围,以便它知道应扫描哪个地址范围以获取更多指针。
带有垃圾回收的语言具有大量基础设施来处理此问题。C ++没有。
Boehm的GC是通常可以得到的最接近的解决方案,它是保守的,如果某些东西可能是指针,则垃圾回收器假定它是一个指针,这意味着一些数据被不必要地保持活动状态。因此,它可能会保持应该进行垃圾回收的数据。
或者,当然,所有这些基础设施原则上都可以添加到C ++编译器中。标准中没有规定不允许它存在。问题在于,这将是一个重大的性能损失,并消除了很多优化机会。

3

1
你是不是想要类似于smart_ptr这样的东西?

0

Boehm GC

它是用C语言编写的,而不是C++,因为它不会调用析构函数,所以可能并不完全适用于C++,但大概可以胜任。


0

当然可以啊?从垃圾收集器的角度来看,指针算术与数组索引没有什么区别。像异或双向链表这样的东西会搞乱GC,但那不是指针算术,而且还有未定义的行为。此外,Boehm存在,这是一种适用于C ++的垃圾收集器的经验证据。在一段时间内,他们甚至威胁要将GC作为C ++ 0x的可选部分。


0
问题是为什么要这样做?在你认为需要垃圾回收的90%情况下,一个智能指针向量(例如一个在删除对象时也会删除其内容的向量/映射)就足够了。剩下的10%可以通过使用引用计数接口来处理。

1
这只是一个让我感到困惑的理论问题 :) - Khaled Alshaya

0

C++指针算术允许您构造N+1个指向T[N]的指针,&T[0]...&T[N-1]和一个哨兵&T[N-1]+1。它们都是指向T[N]数组对象的指针。在这个意义上,它们类似于其他“内部”指针,比如&foo.bar(对象成员的地址)。

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


-1

是的,可以独立于类型安全实现垃圾回收机制(GC),这在 C++ 中并不是很普遍;但 GC 可用的优化机会将受到语言允许的类型灵活度的限制。

如果愿意接受运行时检查指针操作,从而重新实现语言已放弃的类型安全,则 GC 可以更为积极。目前,带有垃圾回收功能的 C++ 通常仅限于像Boehm-Demers-Weiser GC 这样的保守型垃圾回收器。


保守的集合依赖于所有有效的内存至少有一个有效指针指向它。如果你模糊了一个引用,被引用的内存可能会被释放;但是如果一个随机的单词指向某个内存,它就算作一个引用,并将其保持活动状态。 - Barry Kelly
引用是如何维护的?如果像smart_ptr这样的东西,那么指针就不是原始的了。 - 0xC0DEFACE
保守的收集器可以检查有效内存中每个指针大小的字,以查看它们是否“看起来像”指向有效分配块的指针,例如通过类似于页表的机制 - 将前缀掩码化为指针值的树形结构。对于更激进的精确收集器,至少需要有指针类型信息和指针操作和算术的运行时检查,以获得所需的类型安全级别。 - Barry Kelly
但是类型信息可以由与垃圾收集器协调设计的编译器提供;指针算术类型检查的RTL存根也可以如此。 - Barry Kelly

-2

我不是GC实现方面的专家,但如果允许使用指针算术运算,那么就无法确定内存中的某个地址是否不再被引用。

举个例子

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

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

我已经以一种非常基本的方式使用指针算术来打印内存中随机位置的内容。这意味着机器上的所有内存基本上都是可访问的。如果它是可访问的,那么它就不能被GC回收。

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


1
保守型垃圾回收器可以很好地处理指向随机位置的指针 - 它依赖于操作系统提供给它一个有效内存页面列表,并且不会跟随指向未映射页面的指针。 - Barry Kelly
好的,它不能进入未映射的页面,这可以防止崩溃,但是它怎么知道我不能再引用一块内存了呢?我仍然可以在运行时将任意内存地址分配给crazy_pointer,而垃圾收集器永远不会知道我选择了什么。 - hhafez
简单来说,当你使用rand()的结果作为指针时,你所做的是未定义行为。这种用法在C/C++中是“完全合法”的。根据标准,指针算术运算比许多人想象的要受限制得多。 :) - jalf
你是在告诉我那段代码编译不了吗?它一定会编译的。GC将如何处理上述代码? - hhafez
仅仅因为某些代码可以编译通过,并不代表它是有效的。你的代码完全是无效的,C/C++甚至禁止指向应用程序未分配内存空间的指针(以及越界访问)。 - GManNickG
就像GMan所说,它(可能)会编译,但它不是有效的。语言不需要对您的代码做任何事情。编译器不必编译它,也不必发出错误。在运行时,它不必执行您期望的操作,也不必避免格式化硬盘或下载色情内容。这是未定义的行为。语言标准没有定义此代码的效果应该是什么。欢迎来到C++的奇妙世界 :) - jalf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接