【问题标题】:Can you have memory leaks with a garbage collector?垃圾收集器会导致内存泄漏吗?
【发布时间】:2012-05-21 14:17:11
【问题描述】:

如果我有一个垃圾收集器,它可以跟踪每个分配的对象,并在它们不再有可用的引用时立即释放它们,你还会有内存泄漏吗?

考虑到内存泄漏是没有任何引用的分配是不是不可能或者我错过了什么?

编辑:所以我算作内存泄漏的是您在代码中不再有任何引用的分配。您仍然参考的大量累积分配不是我在这里考虑的泄漏。

我也只是在谈论最先进的 G.C.,这已经有一段时间了,但我知道像循环引用这样的情况不会让他们绊倒。我不需要任何语言的具体答案,这只是来自我与朋友的对话。我们谈论的是 Actionscript 和 Java,但我不关心这些特定的答案。

Edit2:从它的声音来看,似乎没有任何原因代码可以完全失去引用分配的能力并且没有 GC 能够拾取它,但我仍在等待更多称重。

【问题讨论】:

  • “内存泄漏”的定义一点也不草率。我建议您通过引用来收紧定义。
  • 尚未投反对票,但由于您没有提供 您的“内存泄漏”定义和您想到的 GC 类型,这并不奇怪。还要检查它是否与某种家庭作业无关,并在需要时添加“家庭作业”标签。
  • 根本没有做作业,只是和朋友谈论actionscript和java,我不知道泄漏是否对他们来说是个问题。
  • 我也更具体地了解了其他定义,谢谢。但是,是的,我一开始就被否决了,但它消失了..
  • 我不认为“内存泄漏”特别模棱两可。本质上,如果程序需要无限量的内存来处理在某个点之后重复无限次的输入序列,则程序存在内存泄漏,即使这些输入只会使其进入有限数量的可观察状态。我想在某些情况下该术语可能不明确(例如,一个程序通过缓冲文件然后输出结果来确定文件中的行数是偶数还是奇数)。可以说,这样的程序只有两个可观察状态。

标签: memory-leaks garbage-collection


【解决方案1】:

如果你的问题真的是这样的:

考虑到内存泄漏不是没有任何引用的分配 那不可能还是我错过了什么?

那么答案是“是的,那是不可能的”,因为正确实施的垃圾收集器将回收所有没有活动引用的分配。

但是,您肯定会在(例如)Java 中出现“内存泄漏”。我对“内存泄漏”的定义是仍然具有活动引用的分配(因此它不会被垃圾收集器回收)程序员没有'不知道该对象是不可回收的(即:对于程序员来说,该对象已死,应该被回收)。一个简单的例子是这样的:

对象A -> 对象B

在此示例中,ObjectA 是代码中正在使用的对象。然而,ObjectA 包含对 ObjectB 的引用,该引用实际上已失效(即:ObjectB 已被分配和使用,现在,从程序员的角度来看,已失效)但程序员忘记将 ObjectA 中的引用设置为 null。在这种情况下,ObjectB 已经“泄露”了。

听起来不是什么大问题,但在某些情况下这些泄漏是累积的。让我们假设 ObjectA 和 ObjectB 实际上是同一个类的实例。每次使用这样的实例时,都会发生程序员忘记将引用设置为 null 的问题。最终你会得到这样的结果:

ObjectA -> ObjectB -> ObjectC -> ObjectD -> ObjectE -> ObjectF -> ObjectG -> ObjectH -> 等等...

现在 ObjectB 到 ObjectH 都被泄露了。像这样的问题(最终)会导致你的程序崩溃。即使使用正确实施的垃圾收集器。

【讨论】:

  • 这个答案似乎最好,但这个例子对我来说有点不清楚:你是说代码有还是没有引用 ObjectA?
  • 还有对 ObjectA 的引用。它仍在使用中。所以它是一个“活的”对象(无论是从 GC 的角度还是从程序员的角度)。但是,有一个死对象链 GC 无法回收,因为 ObjectA 仍然持有对链头的引用。程序员只是忘记取消该引用。对于程序员来说,这些对象应该被回收,但是由于他的健忘(或懒惰),GC不会回收它们。
  • @RobotRocker:说“对象 A 引用了对象 B”和说“对象 A 引用了它实际关心的对象 B”之间存在差异。如果有足够的时间和内存,在没有根对象实际关心的活动对象的百分比将渐近接近 100% 的情况下存在内存泄漏。
  • @supercat,我认为内存泄漏不一定会使程序崩溃,因此如果有足够的时间和记忆。考虑它渐近接近 50% 的情况。该程序现在使用的内存是它需要的两倍。如果可以避免,这就是泄漏。
  • @CaTalyst.X:有很多事情可能会导致程序需要两倍(甚至 10 倍)的内存,因为它“需要”但几乎不会被称为内存泄漏。例如,一个程序创建了很多List<Guid>,但最终在每个项目中平均放置了两个项目,将只使用分配的空间的 1/8 来保存 GUID(即使是这样),但几乎不会“泄漏” ”。我认为,在给定无限输入流的情况下,可以通过有限 RAM 满足其内存需求的程序与不能满足其内存需求的程序之间存在质的差异。
【解决方案2】:

要确定程序是否存在内存泄漏,必须首先定义什么是泄漏。如果存在某些状态S 和一系列输入I,我会将程序定义为存在内存泄漏:

  1. 如果程序处于状态“S”并且接收到输入“I”,它仍将处于状态“S”(如果它没有崩溃),但是...
  2. 重复上述序列“N”次所需的内存量将无限增加。

完全在垃圾收集框架内运行的程序绝对有可能出现上述定义的内存泄漏。发生这种情况的一种常见方式是使用事件订阅。

假设一个线程安全的集合公开了一个CollectionModified 事件,其IEnumerable<T>.GetEnumerator() 方法返回的IEnumerator<T> 在创建时订阅该事件,并在Dispose 上取消订阅;即使在修改集合时,该事件也用于允许枚举继续进行(例如,确保在整个枚举过程中持续存在于集合中的对象将仅返回一次;在其中一部分期间存在的对象将不超过一次返回) .现在假设创建了该集合类的长期实例,并且某些特定输入将导致它被枚举。如果CollectionModified 事件持有对每个未释放的IEnumerator<T> 的强引用,那么重复枚举集合将创建和订阅无限数量的枚举器对象。内存泄漏。

【讨论】:

    【解决方案3】:

    内存泄漏不仅仅取决于垃圾收集算法的效率,如果您的程序保留了具有较长生命周期的对象引用,例如在没有使用的实例变量或静态变量中,您的程序就会发生内存泄漏.

    引用计数存在循环引用含义的已知问题

    Object 1 refers to Object 2 and Object 2 refers to Object 1 
    

    但没有其他人引用对象 1 或对象 2,在这种情况下引用计数算法将失败。

    由于您正在研究垃圾收集器本身,因此值得一读different implementation strategies

    【讨论】:

    • 如果开发者自己检查两个对象是否都被标记为空,如果他们想要删除它,它会起作用吗?
    • 感谢您的链接,我一定会带上阅读材料。我认为大多数 GC 都可以处理循环引用。
    • @RobotRocker 是的,现代 gc 可以处理循环引用计数
    【解决方案4】:

    您可能会以另一种方式使用 GC 导致内存泄漏:如果您使用 conservative garbage collector 天真地扫描内存并且对于看起来像指针的所有内容,不会释放它“指向”的内存,您可能留下无法访问的内存。

    【讨论】:

    • 我正在阅读您提供的链接,但似乎使用的垃圾收集器不太理想。
    • 好吧,如果你总能准确地重建内存中那些可访问的对象的图形,那么你应该使用精确的垃圾收集器,而不是保守的垃圾收集器(这确实“不太理想”) .但是,在实践中使用保守的垃圾收集器,在没有此类信息的实现中,有关详细信息,请参阅此链接:hpl.hp.com/personal/Hans_Boehm/gc/conservative.html
    猜你喜欢
    • 1970-01-01
    • 2018-05-10
    • 2012-06-28
    • 1970-01-01
    • 2012-01-03
    • 1970-01-01
    • 2016-11-02
    • 1970-01-01
    • 2016-03-04
    相关资源
    最近更新 更多