如何在Java中创建内存泄漏?

3720

我最近参加了一次面试,其中被要求使用Java创建一个内存泄漏

不用说,我感到很笨,因为我不知道如何开始创建。

你可以给一个例子吗?


48
具有讽刺意味的是,每个非平凡的Java程序所面临的更难的问题是如何创建内存泄漏! - Peter - Reinstate Monica
3
不断往容器中添加新对象,却忘记添加删除它们的代码,或者实现部分工作的代码无法在程序运行时清理所有对象。 - Galik
5
Java服务器系统中最常见的内存泄漏是在共享状态下发生的,例如请求之间共享的缓存和服务。许多答案似乎过于复杂,忽略了这个明显而常见的领域。可能存在一种常见的泄漏模式,即具有请求作用域键(例如某种自定义缓存)的应用程序范围Map。 - Thomas W
61个回答

2

import sun.misc.Unsafe;
import java.lang.reflect.Field;

public class Main {
    public static void main(String args[]) {
        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            ((Unsafe) f.get(null)).allocateMemory(2000000000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

需要解释一下。例如,这个主要思想/想法是什么?它与之前的答案有何不同? - Peter Mortensen

1

我在javax.swing.JPopupMenu中遇到了一个非常真实的内存泄漏问题。

我有一个GUI应用程序,显示多个选项卡文档。关闭文档后,如果在选项卡上的任何组件上使用了右键单击上下文菜单,则该文档会在内存中停留。菜单在选项卡之间共享,并且事实证明,在调用popupMenu.show(Component invoker, int x, int y)之后,组件会静默地持续作为菜单的“invoker”,直到下一次更改或通过setInvoker(null)清除。间接地,调用者引用会使整个文档及其关联的所有内容持久化。

值得注意的是,菜单只能以这种方式保存对旧组件的一个引用,因此此内存泄漏不会无限增长。


0

在JDK 1.7之前,内存泄漏的一个实时例子:

假设您读取了一个包含1000行文本的文件,并将它们保存在String对象中:

String fileText = 1000 characters from file
fileText = fileText.subString(900, fileText.length());

在上面的代码中,我最初读取了1000个字符,然后使用子字符串获取了最后100个字符。现在fileText应该只引用100个字符,所有其他字符都应该被垃圾回收,因为我失去了对它们的引用,但在JDK 1.7之前,子字符串函数间接地引用了最后100个字符的原始字符串,并防止整个字符串被垃圾回收,直到你失去子字符串的引用,整个1000个字符将一直存在于内存中。
您可以像上面那样创建一个内存泄漏示例。

我不这么认为。一个新的字符串被创建并返回。以下是来自open-jdk 6子字符串函数的代码片段 ((beginIndex == 0) && (endIndex == count)) ? this : new String(offset + beginIndex, endIndex - beginIndex, value); - Karan Khanna
1
正确的新字符串对象被创建,但如果你看到它,它传递的是原始字符串的字符数组值,新创建的字符串保留完整字符数组的引用。您可以比较Java 6到8的实现,Java 7和8使用Arrays.copyOfRange(value,offset,offset+count)返回实际子字符串。 - Praveen Kumar
明白了。谢谢。 - Karan Khanna
“Real-time” 是什么意思? - Peter Mortensen

0

这很简单:

Object[] o = new Object[]{};
while(true) {
    o = new Object[]{o};
}

不是内存泄漏。整个对象堆栈仍然可收集。 - Boann

0
Java内存泄漏的例子之一是MySQL的内存泄漏bug,当忘记调用ResultSets close方法时会导致该问题。例如:
while(true) {
    ResultSet rs = database.select(query);
    ...
    // going to next step of loop and leaving resultset without calling rs.close();
}

0
创建一个只包含 while-true 循环的 JNI 函数,并使用另一个线程中的大对象调用它。GC 不太喜欢 JNI,并会将该对象永久保存在内存中。

-1

您可以尝试使用一个while循环,让许多缓冲读取器同时尝试打开同一个文件,并且条件永远不为假。最后的关键是这些读取器从未关闭。


-2
一个对之前答案的小改进(可以更快地生成内存泄漏)是使用从大型XML文件加载的DOM文档实例。

-7

就像这样!

public static void main(String[] args) {
    List<Object> objects = new ArrayList<>();
    while(true) {
        objects.add(new Object());
    }
}

12
这并不是内存泄漏的例子。你只是试图使用列表占用整个内存。内存泄漏是指过时的引用无法被垃圾回收。 - Karan Khanna

-7

这是一个非常简单的Java程序,它将会耗尽空间。

public class OutOfMemory {

    public static void main(String[] arg) {

        List<Long> mem = new LinkedList<Long>();
        while (true) {
            mem.add(new Long(Long.MAX_VALUE));
        }
    }
}

37
这肯定会耗尽内存,因为要求具有无限量的内存。我不称之为内存泄漏,只是一个愚蠢的程序。 - rds
3
也就是说,这不是内存泄漏,只是分配了太多的内存。 - kritzikratzi

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