class Demo {
private static final LinkedList<Integer> history = new LinkedList<>(Collections.singleton(0));
public static int plusPrevious(int value) {
int result = history.getLast() + value;
history.add(value);
return result;
}
}
import java.util.ArrayList;
import java.util.Collection;
import java.util.Stack;
public class Box <E> {
private final Collection<Box<?>> createdBoxes = new ArrayList<Box<?>>();
private final Stack<E> stack = new Stack<E>();
public Box () {
createdBoxes.add(this);
}
public void put (E e) {
stack.push(e);
}
public E get () {
if (stack.isEmpty()) {
return null;
}
return stack.peek();
}
}
9条答案
按热度按时间raogr8fs1#
在Java中,除非执行以下操作,否则无法真正"泄漏内存":
我想你对最后一种情况感兴趣。常见的情况是:
一个很好的例子是:
`
注册的动作什么也不做,但它会导致模态窗口永远停留在内存中,甚至在关闭后,从而导致泄漏-因为侦听器从未被注册,每个匿名的内部类对象都拥有一个对外部对象的引用(不可见)。更重要的是-任何从模态窗口引用的对象也有泄漏的机会。
这就是为什么像EventBus这样的库默认使用弱引用的原因。
除了监听器,其他典型的例子是缓存,但我想不出一个好的例子。
t30tvxxf2#
首先,我们必须就内存泄漏实际上是什么达成一致。
维基百科used to describe a memory leak像这样:
计算机科学中的内存泄漏(英语:Memory Leak)是指计算机程序消耗内存,但无法将其释放回操作系统。
然而,这已经改变了多次,现在(02/2023)它说:
在计算机科学中,内存泄漏(英语:Memory Leak)是一种资源泄漏,发生在计算机程序错误地管理内存分配,使得不再需要的内存不被释放。
根据上下文,您需要更精确地指定您要查找的内容。
无法访问动态分配的内存
首先,让我们快速浏览一个没有自动内存管理的语言示例:在C语言中,你可以使用
malloc()
来分配内存。这个函数返回一个指向已分配内存的指针。你必须恰好在这个指针上调用free()
,以便将内存释放回操作系统。但是如果这个指针在多个地方使用呢?谁负责调用free()
?如果你过早地释放内存,那么你的应用程序的某些部分仍然在使用这个内存就被破坏了。2如果你不释放内存,你就有了一个漏洞。3如果所有指向内存分配的指针都丢失了(覆盖或超过寿命),则应用程序将无法将内存释放回操作系统。这将满足Wikipedia在2011年对内存泄漏的旧定义。为了避免这种情况,您需要某种类型的契约来定义谁负责释放已分配的内存。这需要文档,必须阅读,可能许多人正确理解和遵循,从而产生各种错误机会。自动内存管理(Java有)把你从这种危险中解脱出来,在Java中你可以使用关键字
new
分配内存,但是在Java中没有free
,new
返回一个"引用",其中(在此上下文中)的行为类似于指针。当所有对已分配内存的引用丢失时(覆盖或超过使用寿命),则系统会自动检测到该问题,并将内存返回给操作系统。在Java中,这种类型的内存泄漏仅在垃圾收集器、JNI模块或类似模块出现错误时才"可用",但至少在理论上是安全的。
其他编程错误
当然,无论是否使用自动内存管理,都可以主动维护不需要的引用。假设下面的类:
每当有人调用
plusPrevious
时,history
-List就会增长。但是为什么呢?只需要一个值,而不是完整的历史记录。这个类占用了它不需要的内存。这满足了Wikipedia对内存泄漏的当前定义。在这种情况下,错误是显而易见的。然而,在更复杂的场景中,决定什么仍然"需要",什么不是那么容易。
无论如何,将内容放入
static
变量是陷入麻烦的"好"开始。如果在上面的例子中history
不是static
,那么该类的用户可能最终释放对Demo
示例的引用,从而释放内存。然而,由于它是静态的,历史将一直挂起,直到应用程序作为一个整体终止。wbgh16ku3#
这里有一个简单的例子
c90pui9n4#
use:
然后添加(大的)数组而不删除它们。在某个时候,你会毫无疑问地耗尽内存。(你可以对任何对象这样做,但是对于大的、满的数组,你会更快地耗尽内存。)
在Java中,如果你解引用一个对象(它超出了作用域),它就会被垃圾收集,所以你必须保持对它的引用,这样才会有内存问题。
qrjkbowd5#
1.在类范围内创建对象集合
1.定期向集合中添加新对象
1.不要删除对保存集合的类的示例的引用
由于始终存在对集合和拥有该集合的对象的示例的引用,因此垃圾回收器永远不会清理该内存,从而随着时间的推移会导致“泄漏”。
13z8s7eq6#
从我读到的投票最多的答案来看,你很可能是在问一个类似C的内存泄漏问题。好吧,既然有垃圾收集,你就不能分配一个对象,丢失它所有的引用,让它仍然占用内存--那将是一个严重的JVM bug。
另一方面,你可能碰巧泄漏线程--当然,这会导致这种状态,因为你可能会有一些线程在运行时引用了对象,你可能会丢失线程的引用。你仍然可以通过API获得线程引用--参见http://www.exampledepot.com/egs/java.lang/ListThreads.html
xeufq47z7#
如果使用下面这个极度人为设计的
Box
类,将会泄漏内存。(准确地说,是在另一次调用put
之后......前提是同一对象没有被重新-put
到其中。)外部世界无法访问它们。它们不能通过此类解引用,然而这个类确保了它们不能被收集。2这是一个真实的的泄漏。3我知道这是人为的,但是类似的情况也可能是偶然的。yzuktlbb8#
尝试以下简单类:
kgqe7b3p9#
看起来大多数的答案都不是C风格的内存泄漏。
我想我应该添加一个库类的例子,这个库类有一个bug,它会给予你一个内存不足的异常。同样,这不是一个真正的内存泄漏,但它是一个你不会想到的内存不足的例子。
您可以在 JDK-4363937: ObjectOutputStream is creating a memory leak 找到原始代码和错误描述