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

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

什么样的例子?


当前回答

JDK 1.7之前内存泄漏的实时示例:

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

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

在上面的代码中,我最初读取了1000个字符,然后执行了子字符串,只获得最后100个字符。现在,fileText应该只引用100个字符,所有其他字符都应该被垃圾收集,因为我丢失了引用,但是在JDK1.7之前,substring函数间接引用了最后100个字符的原始字符串,并阻止了整个字符串的垃圾收集,而整个1000个字符将一直保存在内存中,直到您丢失了对子字符串的引用。

您可以创建一个类似于上述的内存泄漏示例。

其他回答

我曾经有过一次关于PermGen和XML解析的“内存泄漏”。我们使用的XML解析器(我记不清是哪一个)对标记名执行String.intern(),以加快比较速度。我们的一位客户有一个好主意,不将数据值存储在XML属性或文本中,而是将其存储为标记名,因此我们有了这样一个文档:

<data>
   <1>bla</1>
   <2>foo</>
   ...
</data>

事实上,他们没有使用数字,而是使用更长的文本ID(约20个字符),这些ID是唯一的,每天的使用率为1000万至1000万。这使得每天有200 MB的垃圾,而这些垃圾再也不需要了,也永远不会被GCed(因为它在PermGen中)。我们将permagen设置为512MB,因此内存不足异常(OOME)需要大约两天的时间才能到达。。。

什么是内存泄漏:

这是由错误或不良设计引起的。这是在浪费记忆。随着时间的推移,情况会变得更糟。垃圾收集器无法清理它。

典型示例:

对象缓存是一个很好的起点,可以让事情变得一团糟。

private static final Map<String, Info> myCache = new HashMap<>();

public void getInfo(String key)
{
    // uses cache
    Info info = myCache.get(key);
    if (info != null) return info;

    // if it's not in cache, then fetch it from the database
    info = Database.fetch(key);
    if (info == null) return null;

    // and store it in the cache
    myCache.put(key, info);
    return info;
}

您的缓存不断增长。很快整个数据库就被吸进了内存。更好的设计使用LRUMap(仅将最近使用的对象保存在缓存中)。

当然,你可以让事情变得更加复杂:

使用ThreadLocal构造。添加更复杂的参考树。或由第三方库引起的泄漏。

经常发生的情况:

如果此Info对象引用了其他对象,则这些对象也引用了其他的对象。在某种程度上,您也可以认为这是某种内存泄漏(由糟糕的设计导致)。

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

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

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

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

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

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

这里有一个非常简单的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));
        }
    }
}