能有在Java中的内存泄漏

问题描述 投票:33回答:12

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

java memory memory-management memory-leaks
12个回答
21
投票

能有在Java中的内存泄漏?

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

经典C / C ++内存泄漏发生时的应用程序忽略free或当它们与它做dispose一个对象,它泄漏。循环引用是这个子情况下的应用有困难,知道什么时候free / dispose,而忽略做的结果。相关的问题是应用程序使用一个对象时,它已被释放后,或尝试释放它的两倍。 (你可以把后面的问题内存泄漏,或者只是错误。无论哪种方式...)

Java和其他(fully1)托管语言主要是因为GC需要释放不再可达对象的照顾不从这些问题的困扰。 (当然,不存在悬挂指针和双免费问题,并且周期是不存在问题,因为它们是用于C / C ++“智能指针”等的引用计数方案。)

但在某些情况下,GC在Java将错过(从编程的角度看)应该是垃圾收集的对象。这种情况发生在GC想不通,一个对象不能达到:

  • 该方案的逻辑/状态可能使得不能发生,将使用一些变量的执行路径。开发者可以看到这是显而易见的,但GC不能肯定,并会极为谨慎的一侧(因为它需要)。
  • 程序员可能是错的,而GC是避免什么,否则可能会导致悬挂引用。

(请注意,内存泄漏的Java中的原因可以是简单的,或者相当微妙,见@ jonathan.cone的回答对一些微妙的人最后一个可能涉及到,你不应该依赖于GC应对反正外部资源。 )

无论哪种方式,你可以有地方不需要的对象不能被垃圾收集的情况,并流连占用内存...内存泄漏。

然后是一个Java应用程序或库可以经由需要手动管理的本机代码分配离堆对象的问题。如果应用程序/库车或使用不正确,就可以得到本机内存泄漏。 (例如:Android Bitmap memory leak ......注意到这个问题被固定Android中的更高版本。)


1 - 我暗指几件事情。有些管理语言允许你写非托管代码,你可以创造经典的存储泄漏。其他一些管理的语言(或更精确地实现语言)使用引用计数,而不是正确的垃圾回收。一种基于计数基准存储管理需要的东西(即应用程序)打破周期...不然存储泄漏将接踵而至。


1
投票

是的,它可以是,在上下文中,当一个程序错误地坚持认为将再次从未使用过,因此它不会被GC清理的对象的引用。

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

class MemoryLeak {

    private void startLeaking() throws IOException {
        StringBuilder input = new StringBuilder();
        URLConnection conn = new URL("www.example.com/file.txt").openConnection();

        BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));        

        while (br.readLine() != null) {
            input.append(br.readLine());
        }
    }

    public static void main(String[] args) throws IOException {
        MemoryLeak ml = new MemoryLeak();
        ml.startLeaking();
    }
}

0
投票

一个简单的答案是:你不是用JNI工作JVM就会一直照顾POJO的[普通Java对象]的所有初始化。随着JNI,如果你所做的任何内存分配与本机代码,你必须自己照顾该内存。


0
投票

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

我看到一个数据结构多次Java代码至极店项目,但该项目从来没有从那里取出,填补了内存中,直到一个OutOfMemoryError:

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

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

在64位VM这往往会变得更糟,因为交换的存储空间开始得到填补,直到系统抓取太多的IO操作。


10
投票

那么,考虑到Java使用垃圾收集器收集未使用的对象,你不能有一个悬空指针。但是,你可以继续在范围对象长于它需要,这可能被认为是内存泄漏。更多关于此这里:http://web.archive.org/web/20120722095536/http://www.ibm.com:80/developerworks/rational/library/05/0816_GuptaPalanki/

你接受这样或什么测试?因为这是至少一个A +就在那里。


8
投票

是。当你有一个GC内存泄漏仍然偶可发生。例如,您可以保存这些资源,如数据库的结果集,你必须手动关闭。


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
投票

是的,在你的Java应用程序可以积累记忆随着时间的推移,该垃圾收集器是无法释放的感觉。

通过保持到uneeded /不需要的对象引用,他们永远不会掉馅饼的范围和他们的记忆将不会被要求回来。


3
投票

是的,如果你没有去参考的对象,他们将永远不会被垃圾收集和内存使用情况将会增加。但是,由于Java是如何设计的,这是很难实现的,而在其他一些语言中,这有时很难不实现。

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


2
投票

对的,这是可能的。

在有效的Java存在涉及使用数组实现的堆叠的实例。如果您的弹出操作简单地减少指标值有可能有内存泄漏。为什么?因为你的阵列仍具有弹出的值的参考,你还有到堆栈对象的引用。所以正确的事做这个栈的实现将是明确使用弹出的数组索引显式空分配的参考值弹出。


2
投票

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

  • 一旦你把缓存对象的引用而忘记了它的存在。参考长成为无关紧要之前保留在缓存中。解决办法是代表缓存为WeakHashMap
  • 其中,客户端注册回调并不会在API中明确重新注册。解决办法是唯一的弱引用存储到他们。

2
投票

答案很简单:

一个称职的JVM没有内存泄漏,但更多的内存可以比需要使用,因为不是所有的未使用的对象被垃圾回收,但。此外,Java应用程序本身可以容纳引用他们不再需要的对象,这可能会导致内存泄漏。

© www.soinside.com 2019 - 2024. All rights reserved.