2011-02-13 78 views
26

我得到这个问题很多次。什么是一个好的方法来回答?Java中可能会有内存泄漏

编辑:谢谢大家的所有答案。

+1

这里非常详细的资源:http://www.ibm.com/developerworks/library/j-leaks/ –

+0

这个问题已经被问了很多次你的建议,而事实上,对这个论坛已经有很多答案。 –

+1

好问题。从答案中学到很多东西。 – fastcodejava

回答

19

Can there be memory leak in Java?

答案是:这取决于你在说什么样的内存泄漏吨。

传统的C/C++内存泄漏发生在应用程序忽略对象freedispose并且它泄漏时发生。循环引用是这种情况的一种子情况,在这种情况下,应用程序很难知道何时对free/dispose进行操作,而忽略了这样做。相关问题是应用程序在释放后使用对象的位置,或试图释放对象两次。 (你可以把后面的问题内存泄漏,或者只是错误。无论哪种方式...)

Java和其他(完全)托管语言大多不从这些问题的困扰,因为GC照顾释放不再可到达的对象。 (当然,不存在悬挂指针和双精度问题,并且循环没有问题,因为它们是针对C/C++“智能指针”和其他引用计数方案的。)

但是在某些情况下,Java中的GC将错过(从程序员的角度来看)应该被垃圾回收的对象。当GC无法找出无法达到的对象时,会发生这种情况:

  • 程序的逻辑/状态可能是这样,使用某些变量的执行路径不会发生。开发人员可以看到这一点很明显,但GC不能确定,并且在谨慎的情况下(因为它是必需的)。
  • 程序员可能是错的,而GC正在避免可能导致悬挂引用的内容。

(请注意,在Java内存泄漏的原因可以是简单的,或者相当微妙。看@ jonathan.cone的回答对一些微妙的人最后一个可能牵涉到你不应该依靠外部资源无论如何,你可以有一种情况,不需要的对象不能被垃圾回收,并且四处缠绕着内存......内存泄漏。

然后存在的问题是Java应用程序或库可以通过需要手动管理的本机代码分配堆外对象。如果应用程序/库有错误或使用不正确,则可能导致本机内存泄漏。 (例如:Android Bitmap memory leak ...指出的是,这个问题是固定在Android中的更高版本。)


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

+0

+1的细节。 – fastcodejava

3

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

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

+0

如果您没有引用该对象,则可以使用GC'd。如果你(而不是闭环),那么它不是“泄漏”。 Ipso,事实上。 –

+1

@ T.J。克劳德:这是一个非常严格的“泄漏”解释。并不是一个非常有用的,在那。假设某个类维护着各种对象的内部缓存,但不断重新向缓存中添加新实例,而不是重新使用它们。内存使用量不断增加,并且堆中充满了永远不会再使用的对象,这确实对我来说看起来像是“内存泄漏”。 –

+0

@Anon:我大部分都是轻浮的。但是你的观点实际上是一个很好的答案*,我建议将它加入一个。 –

2

简短的回答:

A competent JVM has no memory leaks, but more memory can be used than is needed, because not all unused objects have been garbage collected, yet. Also, Java apps themselves can hold references to objects they no longer need and this can result in a memory leak.

更短的答案:

Java is a language and can't have memory leaks, only Java Virtual Machines can.

+1

我不认为这有帮助。事实是,Java **应用程序**可以并经常发生内存泄漏。 –

+0

@Stephen,谢谢,我已经更新了我的回应,不太模棱两可。 –

+0

如果JVM在不具有内存的情况下可能会发生内存泄漏? – Pacerier

2

是的,它是可能的。

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

4

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

通过保持对未被占用/不需要的对象的引用,它们将永远不会超出范围,并且它们的内存不会被回收。

8

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

0

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

10

那么,考虑到java使用垃圾收集器来收集未使用的对象,你不能有一个悬挂指针。但是,您可以将一个对象保留在作用域中的时间超过它所需的时间,这可能被认为是内存泄漏。更多信息请点击这里:http://web.archive.org/web/20120722095536/http://www.ibm.com:80/developerworks/rational/library/05/0816_GuptaPalanki/

你正在考虑这个还是什么?因为那里至少有一个A +。

+0

不错,简单的答案。 – vz0

+1

你的链接不起作用,它被打破 –

0

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

我见过很多次的Java代码至极商店物品上的数据结构,但项目从来没有从那里取出,填补了内存中,直到一个OutOfMemoryError:

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

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

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

+0

这不是内存泄漏,它只是糟糕的编程。所有这些对象仍然可以被引用,所以它们不会“泄漏” – ed209

2

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

  • 一旦你把对象的引用在缓存,忘记了它的存在。很久以前,该参考文件仍然保存在缓存中。解决方案是将缓存表示为客户端注册回调并且不显式重新注册它们的API中的缓存为WeakHashMap
  • 。解决方案是只存储对它们的弱引用。