Easiest way to cause memory leak in Java?
Possible Duplicate:
Creating a memory leak with Java
造成Java内存泄漏最容易的方法是什么?
在爪哇,你不能真正"泄露记忆",除非你:
- 实习生字符串
- 生成类
- JNI调用的本机代码中的泄漏内存
- 把你不想要的东西放在某个被遗忘或模糊的地方。
我认为你对最后一个案子感兴趣。常见的情况有:
- 听众,尤其是对内部类
- 高速缓存。
一个很好的例子是:
- 构建一个可启动无限数量模态窗口的Swing GUI;
- 让模式窗口在初始化期间执行如下操作:
ZZU1〔0〕
注册的操作什么也不做,但它会导致模式窗口永远留在内存中,即使在关闭之后也会导致泄漏-因为侦听器从未注册过,并且每个匿名内部类对象都持有对其外部对象的引用(不可见)。更重要的是,从模式窗口引用的任何对象也有泄漏的可能。
这就是为什么像eventbus这样的库在默认情况下使用弱引用的原因。
除了监听器,其他典型的例子是缓存,但我想不出一个好的例子。
"在计算机科学中,当一个计算机程序消耗内存,但无法将其释放回操作系统时,就会发生内存泄漏(或在此上下文中的泄漏)。(维基百科)
简单的答案是:你不能。Java做自动内存管理,并将免费为你不需要的资源。你不能阻止这种事发生。它将始终能够释放资源。在使用手动内存管理的程序中,这是不同的。您可以使用malloc()在c中获得一些内存。要释放内存,您需要malloc返回的指针并对其调用free()。但是,如果您不再拥有指针(被覆盖,或者超过了生存期),那么不幸的是,您无法释放这个内存,从而导致内存泄漏。
到目前为止,所有其他的答案都在我的定义中,而不是真正的记忆泄漏。他们的目标都是用无意义的东西快速填满记忆。但在任何时候,您仍然可以取消对所创建对象的引用,从而释放内存——>无泄漏。Aconrad的答案非常接近,不过我不得不承认,因为他的解决方案实际上是通过无休止的循环强制垃圾收集器"崩溃"。
长期的答案是:通过使用JNI编写Java库,可以获得内存泄漏,JNI可以进行手动内存管理,从而导致内存泄漏。如果调用这个库,Java进程将泄漏内存。或者,您可以在JVM中有bug,以便JVM释放内存。在JVM中可能存在一些bug,甚至可能存在一些已知的bug,因为垃圾收集不是那么简单,但它仍然是一个bug。按设计,这是不可能的。您可能需要一些受这种bug影响的Java代码。对不起,我不知道其中一个,它可能在下一个Java版本中不再是一个bug。
下面是一个简单的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public class finalizer { @Override protected void finalize() throws Throwable { while (true) { Thread.yield(); } } public static void main(String[] args) { while (true) { for (int i = 0; i < 100000; i++) { finalizer f = new finalizer(); } System.out.println("" + Runtime.getRuntime().freeMemory() +" bytes free!"); } } } |
1 | public static List<byte[]> list = new ArrayList<byte[]>(); |
然后添加(大)数组而不删除它们。在某个时刻,您将毫无疑问地耗尽内存。(您可以对任何对象执行此操作,但对于大的、完整的数组,您可以更快地耗尽内存)
在Java中,如果引用对象(超出范围),则会被垃圾收集。所以你必须保存一个对它的引用才能有内存问题。
根据我在最有投票权的答案中读到的,你很可能是在要求一个类似C的内存泄漏。好吧,既然有Garbagge集合,就不能分配一个对象,释放所有的引用,让它仍然占用内存——这将是严重的JVM错误。
另一方面,您可能会碰巧泄漏线程——当然,这会导致这种状态,因为您会有一些线程使用它对对象的引用运行,并且您可能会松脱线程的引用。您仍然可以通过API获取线程引用-http://www.exampledepot.com/egs/java.lang/listreads.html
因为始终存在对集合和拥有集合的对象的实例的引用,垃圾收集器将永远不会清除该内存,从而导致一段时间内的"泄漏"。
如果使用以下极其人为的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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(); } } |
似乎大多数答案都不是C型内存泄漏。
我想我会添加一个带有bug的库类示例,它会给您一个内存不足的异常。同样,它不是一个真正的内存泄漏,但它是一个耗尽了您不希望看到的内存的例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public class Scratch { public static void main(String[] args) throws Exception { long lastOut = System.currentTimeMillis(); File file = new File("deleteme.txt"); ObjectOutputStream out; try { out = new ObjectOutputStream( new FileOutputStream("deleteme.txt")); while (true) { out.writeUnshared(new LittleObject()); if ((System.currentTimeMillis() - lastOut) > 2000) { lastOut = System.currentTimeMillis(); System.out.println("Size" + file.length()); // out.reset(); } } } catch (Exception e) { e.printStackTrace(); } } } class LittleObject implements Serializable { int x = 0; } |
您可以在以下位置找到原始代码和错误描述:
http://bugs.sun.com/bugdatabase/view_bug.do?BugSyID=4363937
尝试这个简单类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public class Memory { private Map<String, List<Object>> dontGarbageMe = new HashMap<String, List<Object>>(); public Memory() { dontGarbageMe.put("map", new ArrayList<Object>()); } public void useMemInMB(long size) { System.out.println("Before=" + getFreeMemInMB() +" MB"); long before = getFreeMemInMB(); while ((before - getFreeMemInMB()) < size) { dontGarbageMe.get("map").add("aaaaaaaaaaaaaaaaaaaaaa"); } dontGarbageMe.put("map", null); System.out.println("After=" + getFreeMemInMB() +" MB"); } private long getFreeMemInMB() { return Runtime.getRuntime().freeMemory() / (1024 * 1024); } public static void main(String[] args) { Memory m = new Memory(); m.useMemInMB(15); // put here apropriate huge value } } |