Java应用程序中是否完全没有内存泄漏?

13

可能重复:
如何在Java中创建内存泄漏?

Java中有一个“垃圾回收器”,但这是否意味着Java应用程序完全不存在内存泄漏?如果不是,它们如何发生以及为什么会发生?

我更感兴趣的是使用JavaSE的应用程序中的场景。


这里有一个线程讨论如何在Java中创建内存泄漏:[https://dev59.com/pmw15IYBdhLWcg3wqNgA]。 - highlycaffeinated
5个回答

17
不会 - Java 中仍然可能存在内存泄漏,只是一种“不同类型”的内存泄漏。 维基百科:内存泄漏 计算机科学中的内存泄漏(或在该上下文中的泄漏)是指计算机程序消耗内存但无法将其(内存)释放回操作系统的情况。
在 Java 中,通常是因为未使用/不需要的对象永远不会被重新声明而导致内存泄漏。例如,一个对象可能存储在全局列表中,即使以后永远不访问该对象,也从未被移除。在这种情况下,JVM 不会释放对象/内存 - 它不能 - 因为该对象可能稍后需要,即使它永远不需要。
(顺便说一下,某些对象,例如直接分配的 ByteBuffer,还会消耗“JVM 堆外”内存,由于 finalizer 和内存压力的特性,可能无法及时回收。)
在 Java 中,“内存泄漏”是一个语义问题,而不是“在任何情况下都无法释放”的问题。当然,对于有缺陷的 JNI/JNA 代码,一切都无法保证;-)
愉快的编码。

2
作为后续,没有垃圾收集器能够始终回收所有未使用的内存。可以证明无法构建一个能够检测某个内存块是否永远不会再次使用的GC,因此大多数GC使用内存是否可达作为替代指标。 - templatetypedef
谢谢快速回复pst。你提到的问题是正确的,但更多是由于(糟糕的)编程实践,对吧?假设有一些在内存中的对象“确实”符合回收条件,它们一定会被JVM回收吗?肯定会吗? - Bhushan
1
@10101010 在编程中,“可回收对象”指的是那些不被根强引用的对象,它们“某个时刻”将被回收。然而,在具有在finalize方法中释放“本地”内存的对象的情况下,即使它“可以”释放该内存,由于释放不够快,这可能导致[过早的]OOM。 - user166390
1
一个小问题:在完美的JVM中,GC将始终回收任何不再可达的对象。但由于JVM的错误和GC计算可达性的能力限制,有一些方法可以使大量数据(例如ClassLoaders)在GC目的下保持根源,同时仍然不是“可达”的意义上,即普通Java代码无法取消引用并读取相应的对象值。 - Daniel Pryden
我曾经在一个糟糕的观察者模式实现中遇到过这个问题。内存泄漏确实存在。 - santiagobasulto

2

取决于你如何定义内存泄漏。

如果你特指已分配的内存不再被某些内存根引用,那么垃圾回收器最终会清理所有这些内存。

如果你一般地指内存占用量无限增长,那是很容易实现的。只需让某个集合由静态字段引用并不断添加即可。


1

Java中存在内存泄漏的可能性。这里有一篇很好的文章,其中使用了核心Java的示例。从根本上讲,在Java中发生内存泄漏是因为垃圾收集器无法回收一个对象,因为应用程序持有对它的引用,即使对象本身可能不再被使用。在Java中创建内存泄漏的最简单方法是让应用程序持有对某个东西的引用,但不使用它。

在这个示例中,未使用的对象是一个静态列表,向该列表添加内容最终会导致JVM耗尽内存。静态集合通常是“泄漏”的常见来源,因为它们通常具有长寿命和可变性。


1
到目前为止已经有一些不错的回答了。我不想重复那些帖子,所以我只想补充一点,大多数人在考虑这个问题时没有想到通过JNI运行的本地代码中可能存在的泄漏问题。通过JNI运行的本地代码使用JVM的堆空间来分配内存。因此,如果您的应用程序使用通过JNI运行的本地代码并且存在泄漏,则您的应用程序也会出现泄漏。

1
任何具有一个或多个“活”引用的对象都不会被垃圾回收。因此,只要某个变量(静态变量、堆中变量或栈中变量)引用一个对象,该对象将继续占用不可回收的内存空间。
未关闭的资源(如套接字、JDBC连接等)和不断增长的静态集合是一些较为知名的泄漏产生者。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接