在Java中导致内存泄漏的最简单方法?

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


  • 在类范围内创建对象集合
  • 定期向集合中添加新对象
  • 不要删除对保存集合的类的实例的引用
  • 因为始终存在对集合和拥有集合的对象的实例的引用,垃圾收集器将永远不会清除该内存,从而导致一段时间内的"泄漏"。


    如果使用以下极其人为的Box类,则会泄漏内存。这个类中的put对象最终(在另一个调用put后)精确地…如果同一个物体不被重新进入,它就不能被外界所接触。它们不能通过这个类取消引用,但是这个类确保它们不能被收集。这是一个真正的漏洞。我知道这是人为的,但类似的情况也可能是偶然发生的。

    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
    }
    }