这是一个非常基本的问题。我将使用 C++ 和 Java 来制定它,但它确实与语言无关。 考虑 C++ 中的一个众所周知的问题:
struct Obj
{
boost::shared_ptr<Obj> m_field;
};
{
boost::shared_ptr<Obj> obj1(new Obj);
boost::shared_ptr<Obj> obj2(new Obj);
obj1->m_field = obj2;
obj2->m_field = obj1;
}
这是内存泄漏,每个人都知道 :)。解决方案也是众所周知的:应该使用弱指针来打破“refcount interlocking”。还已知该问题原则上不能自动解决。解决它完全是程序员的责任。
但有一个积极的方面:程序员可以完全控制引用计数值。我可以在调试器中暂停我的程序并检查 obj1、obj2 的引用计数并了解存在问题。我还可以在对象的析构函数中设置断点并观察销毁时刻(或发现该对象尚未被销毁)。
我的问题是关于 Java、C#、ActionScript 和其他“垃圾收集”语言的。我可能遗漏了一些东西,但在我看来他们
- 不要让我检查对象的引用计数
- 不要让我知道对象何时被销毁(好的,当对象暴露给 GC 时)
我经常听说这些语言不允许程序员泄漏内存,这就是它们很棒的原因。据我了解,它们只是隐藏了内存管理问题并使其难以解决。
最后,问题本身:
Java:
public class Obj
{
public Obj m_field;
}
{
Obj obj1 = new Obj();
Obj obj2 = new Obj();
obj1.m_field = obj2;
obj2.m_field = obj1;
}
- 是否内存泄漏?
- 如果是:我该如何检测和修复它?
- 如果不是:为什么?
最佳答案
托管内存系统建立在您不想首先跟踪内存泄漏问题的假设之上。与其让它们更容易解决,不如尝试确保它们从一开始就不会发生。
Java 确实有一个术语“内存泄漏”,意思是内存的任何增长都可能影响您的应用程序,但从来没有一点是托管内存无法清理所有内存。
JVM 出于多种原因不使用引用计数
- 如您所见,它无法处理循环引用。
- 它需要大量内存和线程开销才能准确维护。
- 有更好、更简单的方法来处理托管内存的这种情况。
虽然 JLS 不禁止使用引用计数,但它并未在任何 JVM AFAIK 中使用。
相反,Java 会跟踪许多根上下文(例如每个线程堆栈),并且可以跟踪哪些对象需要保留,哪些可以根据这些对象是否可强访问而丢弃。它还为弱引用(只要对象未被清理就会保留)和软引用(通常不会被清理,但可以由垃圾收集器自行决定)提供便利
关于java - 垃圾收集与手动内存管理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16315959/