我刚刚接受了一次采访,被要求用Java创建内存泄漏。

不用说,我觉得自己很傻,不知道如何开始创作。

什么样的例子?


当前回答


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

其他回答

理论上你不能。Java内存模型阻止了这一点。但是,因为必须实现Java,所以可以使用一些警告。这取决于您可以使用什么:

如果可以使用本机,则可以分配以后不会放弃的内存。如果这是不可用的,那么Java有一个不为人知的小秘密。您可以请求一个不由GC管理的直接访问数组,因此可以很容易地用于造成内存泄漏。这由DirectByteBuffer提供(http://download.oracle.com/javase/1.5.0/docs/api/java/nio/ByteBuffer.html#allocateDirect(int))。如果不能使用其中任何一个,仍然可以通过欺骗GC来造成内存泄漏。JVM是使用一代垃圾收集来实现的。这意味着垃圾堆被划分为三个区域:年轻人、成年人和老年人。对象创建时从年轻区域开始。随着它被越来越多地使用,它逐渐发展到成人到老年人。最有可能到达接骨木区域的对象不会被垃圾收集。您无法确定对象是否泄漏,如果您请求停止并清理GC,它可能会清理它,但在很长一段时间内,它会泄漏。更多信息请访问(http://java.sun.com/docs/hotspot/gc1.4.2/faq.html)此外,类对象不需要是GC’ed。也许有办法做到这一点。

也许通过JNI使用外部本机代码?

使用纯Java,这几乎是不可能的。

但这是一种“标准”类型的内存泄漏,即您无法再访问内存,但它仍然属于应用程序。相反,您可以保留对未使用对象的引用,或者打开流而不关闭它们。

Java1.6中的String.substring方法会造成内存泄漏。这篇博文解释了这一点:

SubString方法在Java中的工作原理-JDK1.7中修复了内存泄漏

这是一个简单/险恶的http://wiki.eclipse.org/Performance_Bloopers#String.substring.28.29.

public class StringLeaker
{
    private final String muchSmallerString;

    public StringLeaker()
    {
        // Imagine the whole Declaration of Independence here
        String veryLongString = "We hold these truths to be self-evident...";

        // The substring here maintains a reference to the internal char[]
        // representation of the original string.
        this.muchSmallerString = veryLongString.substring(0, 1);
    }
}

因为子字符串指的是原始字符串的内部表示,所以原始字符串会保留在内存中。因此,只要你有一个StringLeaker在玩,你的记忆中也有整个原始字符串,即使你可能认为你只是在保存一个字符串。

避免存储对原始字符串的不需要的引用的方法如下:

...
this.muchSmallerString = new String(veryLongString.substring(0, 1));
...

为了增加坏处,您还可以.intern()子字符串:

...
this.muchSmallerString = veryLongString.substring(0, 1).intern();
...

这样做将在内存中保留原始的长字符串和派生的子字符串,即使在StringLeaker实例被丢弃之后也是如此。

我在Java中看到的大多数内存泄漏都与进程不同步有关。

进程A通过TCP与B对话,并告诉进程B创建一些东西。B向资源发出一个ID,比如432423,A将其存储在一个对象中,并在与B对话时使用。在某些情况下,A中的对象会被垃圾收集回收(可能是由于错误),但A从不告诉B这一点(可能是另一个错误)。

现在A不再拥有它在B的RAM中创建的对象的ID,B也不知道A不再引用该对象。实际上,对象是泄漏的。