Java中是否存在内存泄漏?

时间:2011-02-13 22:31:21

标签: java memory memory-management memory-leaks

我多次问这个问题。什么是回答的好方法

12 个答案:

答案 0 :(得分:21)

  

Java中是否存在内存泄漏?

答案是,这取决于你所谈论的内存泄漏类型。

当应用程序在完成对象时忽略对象freedispose并且它泄漏时,会发生经典C / C ++内存泄漏。循环引用是这种情况的子案例,其中应用程序很难知道何时free / dispose,并忽略了这样做。相关问题是应用程序在释放后使用对象或尝试将对象释放两次。 (你可以把后面的问题称为内存泄漏,或者只是错误。无论哪种方式......)

Java和其他(完全 1 )托管语言主要是不会遇到这些问题,因为GC负责释放不再可访问的对象。 (当然,悬空指针和双重自由问题不存在,循环不成问题,因为它们适用于C / C ++“智能指针”和其他引用计数方案。)

但是在某些情况下,Java中的GC会遗漏那些(从程序员的角度来看)应该被垃圾收集的对象。当GC无法确定无法访问对象时会发生这种情况:

  • 程序的逻辑/状态可能使得不会发生使用某些变量的执行路径。开发人员可以看到这一点很明显,但GC无法确定,并且谨慎(因为它是必需的)。
  • 程序员可能错了,GC正在避免可能导致悬空参考的原因。

(请注意,Java中内存泄漏的原因可能很简单,也可能非常微妙;请参阅@ jonathan.cone的一些微妙的答案。最后一个可能涉及您不应该依靠GC来处理。)

无论哪种方式,你都可能遇到这样的情况,即不需要对垃圾进行垃圾收集,并且在内存中占用内存......内存泄漏。

然后存在一个问题,即Java应用程序或库可以通过需要手动管理的本机代码来分配堆外对象。如果应用程序/库有错误或使用不当,您可能会收到本机内存泄漏。 (例如:Android Bitmap memory leak ...注意在Android的更高版本中修复了此问题。)


1 - 我暗指几件事。某些托管语言允许您编写非托管代码,您可以在其中创建经典存储泄漏。一些其他托管语言(或更准确地说是语言实现)使用引用计数而不是适当的垃圾收集。基于引用计数的存储管理器需要某些东西(即应用程序)来打破周期......否则会发生存储泄漏。

答案 1 :(得分:10)

好吧,考虑到java使用垃圾收集器来收集未使用的对象,你不能有一个悬空指针。但是,您可以将对象保留在范围内的时间超过其需要的时间,这可能被视为内存泄漏。更多相关信息:http://web.archive.org/web/20120722095536/http://www.ibm.com:80/developerworks/rational/library/05/0816_GuptaPalanki/

您是否对此进行了测试?因为那里至少是A +。

答案 2 :(得分:8)

是。即使您有GC,仍可能发生内存泄漏。例如,您可能会保留必须手动关闭的资源,例如数据库结果集。

答案 3 :(得分:5)

答案是响亮的,但这通常是编程模型的结果,而不是JVM中某些缺陷的指示。当框架的生命周期与正在运行的JVM不同时,这种情况很常见。一些例子是:

  1. Reloading a context
  2. Failing to dereference observers (listeners)
  3. Forgetting to clean up resources after you're finished using them *
  4. * - 数十亿美元的咨询费已经解决了最后一个

答案 4 :(得分:4)

是的,从某种意义上说,Java应用程序可能会随着时间的推移累积内存,垃圾收集器无法释放。

通过维护对未受影响/不需要的对象的引用,它们永远不会超出范围,并且不会回复它们的记忆。

答案 5 :(得分:3)

是的,如果您不取消引用对象,它们将永远不会被垃圾收集,并且内存使用量会增加。但是由于java的设计方式,这很难实现,而在其他一些语言中,这有时难以实现。

编辑:阅读Amokrane的链接。这很好。

答案 6 :(得分:2)

答案简短:

  

一个称职的JVM没有内存   泄漏,但可以使用更多的内存   比需要的,因为并非所有未使用的   对象已被垃圾收集,   然而。此外,Java应用程序本身可以保存对不再需要的对象的引用,这可能会导致内存泄漏。

答案更短:

  

Java是一种语言而不能拥有   内存泄漏,只有Java Virtual   机器可以。

答案 7 :(得分:2)

是的,这是可能的。

在Effective Java中,有一个涉及使用数组实现的堆栈的示例。如果您的弹出操作只是递减索引值,则可能会发生内存泄漏。为什么?因为您的数组仍然具有对弹出值的引用,并且您仍然具有对堆栈对象的引用。因此,对于此堆栈实现,正确的做法是使用弹出数组索引处的显式空赋值来清除对弹出值的引用。

答案 8 :(得分:2)

本书Effective Java给出了“内存泄漏”的另外两个原因:

  • 将对象引用放在缓存中并忘记它就在那里。在变得不相关之前,引用保留在缓存中。解决方案是将缓存表示为WeakHashMap
  • 在API中,客户端注册回调并且不会显式重新注册它们。解决方案是仅存储对它们的弱引用。

答案 9 :(得分:1)

是的,在某种情况下,如果程序错误地持有对某个对象的引用,而该引用将永远不会再次使用,因此它不会被GC清除。

一个例子就是忘记关闭打开的流:

arr

答案 10 :(得分:0)

一个简单的答案是:只要您不使用JNI,JVM就会处理POJO的[普通旧Java对象]的所有初始化。使用JNI,如果您使用本机代码进行了任何内存分配,则必须自己处理内存。

答案 11 :(得分:0)

是。内存泄漏是应用程序未释放到内存管理器的未使用内存。

我已经多次看到Java代码将项目存储在数据结构上,但项目从未从那里删除,填充内存直到OutOfMemoryError:

void f() {
    List<Integer> w = new ArrayList<Integer>();
    while (true) {
         w.add(new Integer(42));
    }
}

虽然这个例子太明显了,但Java内存错误往往更加微妙。例如,使用依赖注入在component with SESSION scope上存储一个巨大的对象,而在不再使用该对象时不释放它。

在64位虚拟机上,由于交换内存空间开始填满,直到系统爬过太多的IO操作,这往往会变得更糟。