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

时间:2011-02-09 17:49:44

标签: java memory-leaks

  

可能重复:
  Creating a memory leak with Java

导致java内存泄漏的最简单方法是什么?

9 个答案:

答案 0 :(得分:29)

除非你:

,否则你无法在Java中“泄漏内存”
  • 实习生字符串
  • 生成课程
  • 由jni
  • 调用的本机代码中的内存泄漏
  • 在某个被遗忘或模糊不清的地方保留对你不想要的东西的引用。

我认为你对最后一案感兴趣。常见的情况是:

  • 听众,特别是内部类
  • 高速缓存。

一个很好的例子是:

  • 构建一个Swing gui,启动可能无限数量的模态窗口;
  • 让模态窗口在初始化过程中执行类似的操作:
    StaticGuiHelper.getMainApplicationFrame().getOneOfTheButtons().addActionListener(new ActionListener(){
      public void actionPerformed(ActionEvent e){
         // do nothing...
      }
    })
    

注册的操作什么都不做,但它会导致模态窗口永远停留在内存中,即使在关闭之后也会导致泄漏 - 因为侦听器永远不会被注册,并且每个匿名内部类对象都拥有一个引用(不可见)它的外在物体。更重要的是 - 从模态窗口引用的任何对象都有可能泄漏。

这就是为什么像EventBus这样的库默认使用弱引用。

除了听众之外,其他典型的例子都是缓存,但我想不出一个很好的例子。

答案 1 :(得分:15)

“当计算机程序消耗内存但无法将其释放回操作系统时,会发生计算机科学(或泄漏,在此上下文中)的内存泄漏。” (维基百科)

简单的答案是:你做不到。 Java执行自动内存管理,并将释放您不需要的资源。你不能阻止这种情况发生。它总是能够释放资源。在具有手动内存管理的程序中,这是不同的。你可以使用malloc()在C中获得一些内存。要释放内存,您需要malloc返回的指针并在其上调用free()。但是如果你不再使用指针(覆盖或超过生命周期),那么很遗憾你无法释放这些内存,从而导致内存泄漏。

到目前为止,所有其他答案都在我的定义中并非真正的内存泄漏。它们都旨在快速填充无意义的内存。但是在任何时候你仍然可以取消引用你创建的对象,从而释放内存 - >没有泄漏。 acconrad的答案非常接近,但我不得不承认,因为他的解决方案实际上只是通过强制它在无限循环中“崩溃”垃圾收集器。

答案很长:您可以通过使用JNI编写Java库来获取内存泄漏,JNI可以进行手动内存管理,从而导致内存泄漏。如果你调用这个库,你的java进程将泄漏内存。或者,您可能在JVM中有错误,因此JVM会丢失内存。 JVM中可能存在bug,甚至可能有一些已知的bug,因为垃圾收集不是那么简单,但它仍然是一个bug。按设计这是不可能的。你可能会要求一些受这种bug影响的java代码。对不起,我不知道一个,不管怎样,在下一个Java版本中它可能不再是一个bug。

答案 2 :(得分:11)

这是一个简单的例子

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!");
        }
    }
}

答案 3 :(得分:8)

public static List<byte[]> list = new ArrayList<byte[]>();

然后添加(大)数组而不删除它们。在某些时候,你会在没有怀疑的情况下耗尽内存。 (您可以对任何对象执行此操作,但使用大型完整阵列可以更快地耗尽内存)

在Java中,如果取消引用对象(它超出范围),则会对其进行垃圾回收。因此,您必须持有对它的引用才能出现内存问题。

答案 4 :(得分:3)

  1. 在课程范围内创建对象集合
  2. 定期将新对象添加到集合
  3. 不要删除对包含集合的类的实例的引用
  4. 因为始终存在对集合的引用以及拥有集合的对象的实例,垃圾收集器将永远不会清理该内存,从而导致“泄漏”。

答案 5 :(得分:3)

从我在最多投票的答案中读到的内容,你很可能会要求类似C的内存泄漏。好吧,因为有garbagge集合,你不能分配一个对象,松散它的所有引用并使它仍然占用内存 - 这将是严重的JVM错误。

另一方面,你可能碰巧泄漏线程 - 这当然会导致这种状态,因为你会有一些线程运行它对对象的引用,你可能会松开线程的引用。您仍然可以通过API获取Thread引用 - http://www.exampledepot.com/egs/java.lang/ListThreads.html

答案 6 :(得分:1)

如果使用的话,下面非常人为的Box类会泄漏内存。对put进入此类的对象最终(在另一次调用put之后是准确的...如果相同的对象没有重新put进入它。)外部无法访问世界。它们不能通过这个类解除引用,但是这个类确保它们不能被收集。这是一个真正的泄漏。我知道这是非常人为的,但类似的情况可能是偶然的。

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();
    }
}

答案 7 :(得分:0)

试试这个简单的课程:

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

答案 8 :(得分:0)

似乎大多数答案都不是C风格的内存泄漏。

我以为我会添加一个库类的示例,其中包含一个会导致内存不足异常的错误。同样,它不是真正的内存泄漏,而是一个你不会期望的内存不足的例子。

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?bug_id=4363937