Java:内存泄漏与列表的陌生感

Cod*_*ler 1 java memory-leaks loops list

我正在尝试使用Java及其允许内存泄漏的能力.我写出了这个简单的代码:

import java.util.ArrayList;
import java.util.List;

public class Test {

public static void main(String[] args) {

    class Obj {
        int i;

        Obj(int i) {
            this.i = i;
        }
    }

    List<Obj> list;
    while(true) {
        list = new ArrayList<Obj>();
        for(int i = 0; i < 1000; i++) {
            Obj o = new Obj(i);
            list.add(o);
        }

        try {
            Thread.sleep(1); //<-- added to give the gc time to trash the previous iteration
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
}
Run Code Online (Sandbox Code Playgroud)

执行此操作时(特别是每次迭代时将更多对象添加到列表中),您可以快速查看使用的内存量.我能够非常快速地达到2千兆字节,每次迭代都会添加10000个对象.看起来好像列表(以及利用"链接列表"列表的其他类型的对象,如函数)不喜欢被删除.

当我没有将对象添加到列表中时,内存堆根本没有增加(意味着垃圾处理正在完成它的工作).我尝试将列表重置为每次迭代时为null,然后重新声明它以及调用clear()方法.似乎没什么用.每当我在循环中使用List时,我的RAM就会呼救.

那么,为什么会这样呢?为什么垃圾处理不会在每次迭代时都摆脱列表而不是让它们堆叠起来?List接口是否不允许这种用法?我只是不给垃圾处理足够的时间摆脱最后一例?

NPE*_*NPE 5

你的代码很好.除非你在JVM中遇到了一些晦涩难懂的漏洞,否则也没有内存泄漏.可能发生的事情是你给了JVM很多内存,并且决定利用那个内存.

在尝试了代码之后,我能够重现您所看到的内容的唯一方法是配置一个非常大的年轻代堆(-Xmn).在年轻代已满之前,JVM无需运行垃圾收集器,因此该过程最终会占用大量内存.

但是,当年轻代堆变满时,GC会运行并收集所有无法访问的对象.

在下面的屏幕截图中,右上角的图显示了堆大小:

VisualVM的

正如你所看到的,在19:47:50之前有一个大幅下降.这是年轻一代充满的时候,当GC运行并收集了所有旧列表时.

请注意,如果您使用OS工具来监视内存使用情况,则在运行垃圾收集器时可能看不到丢弃.当释放一些堆对象时,它们曾经占用的内存通常不会被释放回操作系统.但是,它可以通过相同的过程重复使用.

如果内存使用是一个问题,您需要重新访问您提供给JVM的选项.