【问题标题】:Can garbage collection coexist with explicit memory management?垃圾收集可以与显式内存管理共存吗?
【发布时间】:2010-09-19 04:11:27
【问题描述】:

例如,假设在 C# 4 中包含一个“删除”关键字。由于引用,是否可以保证您永远不会有野指针,但仍然能够依赖垃圾收集器系统?

我可以看到它可能发生的唯一方法是,如果不是对内存位置的引用,而是对指向实际对象的指针表的索引。但是,我确信在某些情况下会破坏,并且有可能破坏类型安全/有悬空指针。

编辑:我说的不仅仅是.net。我只是以 C# 为例。

【问题讨论】:

标签: language-agnostic garbage-collection computer-science theory


【解决方案1】:

使用垃圾回收,只要您有对对象的引用引用,它就会保持活动状态。手动删除无法保证。

示例(伪代码):

obj1 = new instance;
obj2 = obj1;

// 

delete obj2;
// obj1 now references the twilightzone.

简而言之,将手动内存管理与垃圾收集相结合违背了 GC 的目的。此外,何苦呢?如果你真的想拥有控制权,请使用 C++ 而不是 C#。 ;-)。

【讨论】:

  • 在上述示例中将 mmm 与 gc 结合的一种方法(您的意思是 obj2 = obj1 吗?)是当引用计数变为 0 时,删除只是将对象标记为立即删除。跨度>
  • @quamrana:那为什么还要打电话给删除呢?如果它仍然等待引用计数变为零,它就不再是手动的了。
  • 不适用于分代垃圾收集。我认为有一种情况可以将对象从受分代 gc 转换为引用计数 gc。
【解决方案2】:

你能得到的最好的方法是划分成两个“半球”,其中一个半球被管理,并且可以保证没有悬空指针。另一个半球有明确的内存管理并且不提供任何保证。这两者可以共存,但是不行,你不能给第二半球强硬的保证。您所能做的就是跟踪所有指针。如果一个被删除,那么指向同一实例的所有其他指针都可以设置为零。不用说,这是相当昂贵的。您的表会有所帮助,但会引入其他成本(双重间接)。

【讨论】:

  • 或者对非托管半球中的所有内容使用弱引用,并在取消引用时自动提升它们。也很昂贵,但将一些工作从删除中转移出来,因此有时可能会更便宜。
  • 哎呀,指针跟踪会很好,但可能会很昂贵(如果你已经有垃圾收集,那就没用了)。
  • 如果您使用的是 C++/CLI,则此模型已经存在。您可以在 C++ 堆上分配非托管对象,在托管堆上分配托管对象,并且它们之间有指针。
【解决方案3】:

你可以 - 有点:让你的对象一次性,然后自己处理。

手动删除不太可能提高托管环境中的内存性能。它可能有助于非托管资源,处置的全部内容。

我宁愿实现和使用 Disposable 对象变得更容易。我不知道这应该是什么样子,但是在 .NET 下管理非托管资源是一个冗长的痛苦。


实现删除的一个思路: delete 标记一个对象以进行手动删除。在下一个垃圾回收周期中,该对象被删除,所有对它的引用都设置为 null。

一开始听起来很酷(至少对我而言),但我怀疑它是否有用。 这也不是特别安全 - 例如。另一个线程可能正忙于执行该对象的成员方法,这样的方法需要抛出,例如访问对象数据时。

【讨论】:

  • .Net IDisposable“模式”不用于内存管理,而是用于其他非托管资源(如文件句柄等)的生命周期管理。内存管理始终由垃圾收集器处理
  • 虽然 Mendelt 在技术上是正确的,但 .NET IDisposable 模式正好适用于您想要确定性销毁的情况。所以在我看来仍然是一个很好的答案。
  • 为了您描述的安全方法,语言必须支持“自我更改”引用类型,这类似于WeakReference,但没有额外的间接级别。一个相关的概念是“伸缩参考”;特殊类型TelescopingObject 将有一个私有BetterVersion 字段,并且任何对TelescopingObject 的伸缩引用(其BetterVersion 字段为非空)将被GC 更改为对由此标识的对象的引用。这将允许对经过比较并发现相等的字符串进行伸缩引用...
  • ...替换为对同一字符串的引用。创建新字符串时,通常不值得搜索可能与它们匹配的现有字符串,但如果代码发现现有字符串 确实 匹配,则记录该等价将很有帮助 if 可以确保最终不会生成一个长等价链,该等价链被一个可访问但未访问的对象保持活动状态[访问该对象会使链崩溃]。
【解决方案4】:

Chris Sells 也在 .NET Rocks 上讨论过这个问题。我想那是他第一次露面的时候,但后来的采访中可能会重新讨论这个话题。

http://www.dotnetrocks.com/default.aspx?showNum=10

【讨论】:

    【解决方案5】:

    我的第一反应是:为什么不呢?我无法想象你想要做的事情只是在堆上留下一个未引用的块以便稍后再次找到它。好像一个指向堆的四字节指针太多了,以至于无法跟踪这个块。

    所以问题不在于分配未引用的内存,而是有意处置仍在引用中的内存。由于垃圾收集在某些时候执行了将内存标记为空闲的功能,因此我们似乎应该能够调用一个替代的指令序列来处理这个特定的内存块。

    但是,问题出在这里:

    String s = "Here is a string."; 
    String t = s;
    String u = s;
    junk( s );
    

    tu 指向什么?在严格的参考系统中,tu 应该是 null。因此,这意味着您不仅需要进行引用计数,还可能需要进行跟踪

    但是,我可以看到您应该在代码中此时使用s。所以junk 可以将引用设置为null,并通过某种优先级代码将其传递给清扫器。 gc 可以被激活以进行有限的运行,并且只有在无法访问时才释放内存。所以我们不能明确地释放任何人已经编码以再次以某种方式使用的东西。 但是如果s 是唯一的引用,那么块就会被释放。

    所以,我认为它只适用于有限遵守显式方面。

    【讨论】:

      【解决方案6】:

      在 C++ 等非托管语言中是可能的,并且已经实现。基本上,您实现或使用现有的垃圾收集器:当您需要手动内存管理时,您可以正常调用 new 和 delete,而当您需要垃圾收集时,您可以调用 GC_MALLOC 或任何用于垃圾收集器的函数或宏。

      有关示例,请参阅 http://www.hpl.hp.com/personal/Hans_Boehm/gc/

      由于您使用 C# 作为示例,也许您只想在托管语言中实现手动内存管理,但这是为了向您展示相反的可能性。

      【讨论】:

        【解决方案7】:

        如果对象引用上的删除语义会使引用该对象的所有其他引用为空,那么您可以使用 2 级间接(比您提示的多 1 级)来执行此操作。虽然请注意,虽然底层对象将被销毁,但必须在堆上保持固定数量的信息(足以保存引用)。

        用户使用的所有引用都会引用真实对象的隐藏引用(可能存在于堆中)。当对对象进行某些操作时(例如调用方法或依赖其标识,例如使用 == 运算符),程序员使用的引用将取消引用它指向的隐藏引用。删除对象时,实际对象将从堆中删除,隐藏引用将设置为 null。因此,引用程序员会看到评估为 null。

        清除这些隐藏的引用将是 GC 的工作。

        【讨论】:

          【解决方案8】:

          这将有助于处理长寿命对象的情况。当对象在短时间内使用并快速取消引用时,垃圾收集工作得很好。问题是当某些对象存在很长时间时。清理它们的唯一方法是执行资源密集型垃圾回收。

          在这些情况下,如果有一种方法可以显式删除对象,或者至少有一种方法可以将对象图移回第 0 代,事情会变得容易得多。

          【讨论】:

            【解决方案9】:

            是的……但有一些滥用。

            可以稍微滥用 C# 来实现这一点。 如果您愿意使用Marshal 类、StructLayout 属性和unsafe code,您可以编写自己的手动内存管理器。

            您可以在此处找到该概念的演示:Writing a Manual Memory Manager in C#

            【讨论】:

            • 不需要使用编组,真的。可以定义一种结构类型,其中包括数据保存结构的静态数组,并让每个实例简单地保存该数组的索引。这种结构的行为类似于引用(结构的副本将访问与原始结构相同的数组元素),但代码可以从池中分配结构并将释放的结构放回池中(如果初始分配足够大)必须在类型初始化完成后在 GC 堆上分配任何东西。
            猜你喜欢
            • 2013-04-25
            • 2011-02-28
            • 2012-11-30
            • 1970-01-01
            • 1970-01-01
            • 2012-04-30
            • 2011-02-26
            • 2017-03-15
            • 2017-08-22
            相关资源
            最近更新 更多