12

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

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

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

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

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

4

4 回答 4

17

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

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

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

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

对象A -> 对象B

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

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

对象A -> 对象B -> 对象C -> 对象D -> 对象E -> 对象F -> 对象G -> 对象H -> 等等...

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

于 2012-05-16T13:18:44.473 回答
2

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

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

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

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

于 2012-05-17T00:02:26.003 回答
0

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

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

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

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

由于您正在研究垃圾收集器本身,因此值得一读不同的实现策略

于 2012-05-14T04:42:14.120 回答
0

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

于 2012-05-17T15:16:57.607 回答