0

好的,我在特定情况下遇到问题,我的程序从堆空间中获取内存不足错误。

假设我们有两个 ArrayList,第一个包含许多T对象,第二个包含从第一个 List 的对象W创建的T对象。

我们以这种方式循环遍历它(在循环列表之后:

public void funct(ArrayList<T> list)
{
  ArrayList<W> list2 = new ArrayList<W>();
  for (int i = 0 ; i < list.size() ; i++)
  {
     W temp = new W();
     temp.set(list.get(i));
     temp.saveToDB();
     list2.add(temp);          
  }

  // more code! from this point on the `list` is useless
}

我的代码与这个非常相似,但是当list包含大量对象时,我经常将堆空间从内存中取出(在 for 循环期间),我想解决这个问题。

我不太清楚GCjava中的工作原理,但是在前面的示例中肯定有很多可能的优化。由于在listfor 循环之后不再使用,我认为这是第一个优化,在我们循环通过它时从for loopto更改do loop并清空:list

public void funct(ArrayList<T> list)
{
  ArrayList<W> list2 = new ArrayList<W>();
  while (list.size() > 0)
  {
     W temp = new W();
     temp.set(list.remove(0));
     temp.saveToDB();
     list2.add(temp);
  }

  // more code! from this point on the `list` is useless
}

这个修改有用吗?如何对上述代码进行更好的优化?以及如何防止堆空间内存不足错误?(增加XMXandXMS值是不可能的)。

4

4 回答 4

1

如果您进行了一些内存分析,您会发现堆耗尽的最大来源是W实例,您可以通过将它们添加到list2. 它ArrayList本身为每个包含的对象增加了非常小的开销(如果适当地预先调整大小,只有 4 个字节,最坏的情况是 8 个字节),所以即使你保留list了,这也没什么大不了的。

W如果不改变您在循环中创建的每个实例的非保留方法,您将无法减轻堆压力。

于 2014-01-20T11:20:22.910 回答
1

这真的取决于很多事情。W 和 T 物体有多大?您肯定可以做的一项优化是 ArrayList list2 = new ArrayList(list.size()); 这样你的 listarray 不需要多次调整它的大小。这不会有太大的不同。真正的问题可能是 W 和 T 对象的大小和数量。您是否考虑过一次使用不同的数据结构来管理一小部分对象?

于 2014-01-20T10:49:57.357 回答
1

您可以尝试设置-XX:MaxNewSize=40% of you XmxAND-XX:NewSize=40% of you Xmx 此参数将加速 GC 调用,因为您的创建率很高。

如需更多帮助:请在此处查看

于 2014-01-20T10:50:48.683 回答
0

您继续引用原始列表中的所有项目:

temp.set(list.get(i)) // you probably store somewhere the passed reference

如果 T 对象的尺寸很大并且您不需要它的所有字段,请尝试使用它的投影。

temp.set( extractWhatINeed( list.get(i) ) )

这将涉及创建一个字段少于 T(提取方法的返回类型)的新类。

现在,当您不引用原始项目时,它们有资格获得 GC(当列表本身不再被引用时)。

于 2014-01-20T10:56:39.730 回答