2

在 .NET/Java 中,您可以将引用包装在具有与垃圾收集器有关的神奇属性的类中(例如 WeakReference 等)

对于“不遵循”引用的想法,似乎没有这样的类 - 即一种告诉垃圾收集器从指定引用开始有一个复杂的对象子图并且不应该收集它的方法。(所以不要浪费 CPU 周期来标记和扫描该子图)。

有谁知道为什么没有这个功能?

非常感谢

4

3 回答 3

1

该功能不存在,因为它不会按照您的想法进行。标记和清除收集器遍历对象图并记录应该保留哪些对象。如果您告诉它不要标记某些子图,那么收集器会在它执行任何清理操作时丢弃所有这些对象。唯一有用的引用状态是“保留这个”(在主类中锚定的图表中保留一个引用)、“扔掉这个”(不要保留任何可以从那里到达的引用)和“你可以选择是否扔掉这个离开”(WeakReference)。

于 2013-08-01T21:21:34.143 回答
0

了解 GC 行为的最简单方法是去保龄球馆,观察球员掷出第一个球后会发生什么。机器识别所有仍然站立的瓶,将它们移出车道,盲目地清理车道,然后将站立的瓶放回车道上。机器不关心哪些针被击倒或什至多少(除非所有十个针都被击倒,在这种情况下,它会立即前进到下一帧)。即使有办法标记被击倒的瓶,这种标记对置瓶机也无济于事。

要考虑的另一件事是,GC 框架最基本的优点之一是没有悬空引用这样的东西。只要在任何地方都存在可达引用,对象就会存在,并且在最后一个可达引用被销毁时将不再存在。为了允许弱引用之类的事情,系统首先识别可以通过强引用到达的所有对象。完成后,系统将遍历弱引用列表,并使其目标未被标记为活动的任何弱引用无效。它还将遍历一个对象列表,这些对象覆盖Finalize并将该列表上但未在其他任何地方引用的任何项目添加到需要立即清理的对象列表中。覆盖的对象Finalize或者存在的弱引用将继续存在,直到 GC 消除任何和所有可以通过任何方式到达它的引用;然而,无论 GC 何时运行以回收空间,大多数对象将在最后一个引用被覆盖后立即停止存在。

于 2013-08-27T22:11:24.967 回答
0

问题是 GC 的设计是假设一切都死了并找到的对象,然后收集其余的。为了使您的想法可行,您必须反转方法以使 GC 从所有活动开始并找到对象。

考虑这种情况。您有一个对象 A,即您的特殊“不要浏览我的图表”对象。你的图表是 A->B->C。在找到活动对象的 GC 中,它无法知道 B 和 C 是活动的,因为“不要导航我的图形”标志会阻止它找到 B 和 C。所以 A 保持活动但 B 和 C收集。不好。

那么为什么 GC 会搜索活动对象而不是死对象呢?为了找到活动对象,GC 从它确定仍在使用的引用开始(称为根)。这些通常是堆栈上的引用(当前运行方法中的局部变量)、静态引用和其他一些引用。这些根引用的每个对象,递归地追溯到图的末尾,肯定仍在使用中,无法收集。其他的都是垃圾。

如果您实现了逆向方法并假设一切都还活着并试图搜索死对象,那么您必须查看堆中的每个对象,找出引用它的对象,然后追踪该图直到它用完或找到一个根。如果它找到了根,那么链中的所有东西都是有生命的。如果不是,则链中的所有内容都已死。

这两种方法的区别在于,从根源开始需要的工作要少得多。您遵循的每个参考都保证有效。一个对象可能引用一千个其他对象,但您只需要从根开始穿过它一次即可证明它和这千个对象是活着的。相反,如果您从未从根目录访问过该对象,那么您无需浏览这数千个引用就知道它已经死了。

此外,对象存储它们引用的内容,而不是引用它们的内容。给定一个对象,找出是否有人引用它需要搜索所有其他对象的引用。或者您可以在每个对象上存储一个裁判列表,但现在每个引用的内存是原来的两倍。

在 .Net 中,有 GCHandle 类,它允许您创建一个被视为根的引用。因此不会收集对象的子图。但是仍然必须导航子图以了解这些对象是什么。

简而言之,GC 以这种方式设计是为了简单和性能。如果您不标记/清除对象的子图,则子图中的对象可能会被垃圾收集。这与您想要的相反。

于 2013-08-08T08:17:43.950 回答