6

Java代码如下:

Random r = new Random(1234697890);
HashMap<Integer, List<Integer>> map = new HashMap<Integer, List<Integer>>();
List<Integer> list = new ArrayList<Integer>();

for(int i=0;i<100000;i++){
    for(int j=0;j<1000;j++){
        list.add(r.nextInt(100000));
    }
    map.put(i, list);
    map.remove(i);
}

i达到 37553 时,java.lang.OutOfMemoryError: Java heap space会发生。
似乎垃圾收集不会在循环中发生。
现在我想知道如何解决这个问题。

4

4 回答 4

5

您一直使用相同的 List,当循环退出时,它包含 100000 * 1000 个项目。要使 GC 摆脱您的列表,您需要将其范围缩小到for(i)循环内。

换句话说,在这段代码中,map 和 list 在任何时候都可以访问,因此不符合收集条件。

于 2012-12-05T13:11:31.997 回答
5

尝试如下重写代码,你不应该得到 OOME 的...

Random r = new Random(1234697890);
HashMap<Integer, List<Integer>> map = new HashMap<Integer, List<Integer>>();

for(int i=0;i<100000;i++){
    List<Integer> list = new ArrayList<Integer>();
    for(int j=0;j<1000;j++){
        list.add(r.nextInt(100000));
    }
    map.put(i, list);
    map.remove(i);
}

您的原始代码的问题是:

  • 你只创建一个列表,
  • 你不断地向它添加越来越多的元素,并且
  • 该列表仅在代码完成时才变成垃圾……因为它一直在“范围内”。

在循环内移动list声明意味着ArrayList在每次循环迭代中创建并填充一个新声明,并在您开始下一次迭代时变成垃圾。


有人建议打电话System.gc()。对于您的情况,这根本无济于事,因为要收集的垃圾最少1 个。总的来说,这是一个坏主意,因为:

  • GC 保证在抛出 OOME 之前立即运行,
  • JVM 可以比你更好地计算出运行 GC 的最佳(即最有效)时间是什么时候,
  • 无论如何,您的电话System.gc()可能会被完全忽略。可以配置 JVM,以便System.gc()忽略对的调用。

1 - 我的书呆子想指出map.put(i, list); map.remove(i);最有可能生成一个Integer最有可能成为垃圾的对象。ArrayList但是,与您无限增长的对象相比,这是“鸡饲料” 。

于 2012-12-05T13:19:44.560 回答
0

在您的情况下,您继续填充相同的list内容(即使您从该 HashMap 中删除它,它仍然作为局部变量存在)。

JVM 承诺在抛出OutOfMemoryError. 因此,您可以确定没有任何东西需要清理。

于 2012-12-05T13:13:33.767 回答
0

你的代码

List<Integer> list = new ArrayList<Integer>();

for(int i=0;i<100000;i++){
    for(int j=0;j<1000;j++){
        list.add(r.nextInt(100000));
    }
    map.put(i, list);
    map.remove(i);
}

是相同的

List<Integer> list = new ArrayList<Integer>();

for(int i=0;i<100000 * 1000; i++) {
    list.add(r.nextInt(100000));
}

如您所见,它是列表,而不是保留所有整数的地图。顺便说一句,试试这个,看看会发生什么;)

list.add(r.nextInt(128));
于 2012-12-05T13:30:39.903 回答