繁体   English   中英

Java中是否存在内存泄漏?

[英]Can there be memory leak in Java

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

Java中可能存在内存泄漏吗?

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

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

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

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

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

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

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

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


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

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

你正在考试这个什么的吗? 因为那里至少有一个A +。

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

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

  1. 重新加载上下文
  2. 未能取消引用观察者(听众)
  3. 使用完毕后忘记清理资源 *

* - 已经解决了数十亿美元的咨询费用

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

通过维护对未被禁止/不需要的对象的引用,它们将永远不会超出范围,并且不会返回它们的内存。

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

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

简短的回答:

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

对的,这是可能的。

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

有效Java ”一书给出了“内存泄漏”的另外两个原因:

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

是的,它可以在一个上下文中,当一个程序错误地持有一个永远不会再使用的对象的引用,因此它不会被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();
    }
}

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

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

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

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

虽然这个例子太明显了,但Java内存错误往往更加微妙。 例如,使用依赖注入在具有SESSION范围组件上存储大对象,而在不再使用该对象时不释放它。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM