Java中字符串的不变性是否会导致内存不足

时间:2012-10-16 08:24:40

标签: java memory memory-management out-of-memory

我编写了一个简单的Java程序,它从数据库读取一百万行并将它们写入文件。

该程序可以使用的最大内存为512M。

我经常注意到这个程序运行Out Of Memory超过500K行。

由于程序是一个非常简单的程序,因此很容易发现它没有内存泄漏。程序的工作方式是从数据库中获取一千行,使用Streams将它们写入文件,然后获取下一千行。每行的大小各不相同,但没有一行是巨大的。在程序运行时进行转储时,可以在堆上轻松看到旧字符串。堆中的这些String无法访问,这意味着他们正在等待收集垃圾。我也相信GC在执行这个程序时不一定会运行,这会使String在堆中的时间超出它们应该的时间。

我认为解决方案是使用长Char Arrays(或Stringbuffer)而不是使用String对象来存储DB返回的行。假设我可以覆盖Char数组的内容,这意味着可以在多次迭代中使用相同的Char数组,而不必每次都分配新的空间。

伪代码:

  1. 使用新char [1000] [1000];
  2. 创建一个数组数组
  3. 将数千行从DB填充到数组中。
  4. 将数组写入文件。
  5. 对下一千行使用相同的数组
  6. 如果上面的伪代码修复了我的问题,那么实际上String类的不可变特性会伤害Java程序员,因为即使字符串不再使用,也没有直接的方法来声明字符串占用的空间。 / p>

    这个问题有更好的替代方案吗?

    P.S:我没有单独进行静态分析。我使用yourkit profiler来测试堆转储。转储显然说96%的字符串没有GC根,这意味着他们正在等待收集垃圾。我也不在代码中使用Substring。

3 个答案:

答案 0 :(得分:2)

班级String的不可变性与OutOfMemoryError完全无关。不变性意味着它永远不会改变,只有那样。

如果内存不足,只是因为垃圾收集器无法找到任何垃圾收集

在实践中,很可能你在内存中持有太多字符串的引用(例如,你是否有任何类型的字符串集合,例如List,Set,Map?)。您必须销毁这些引用以允许垃圾收集器完成其工作并释放一些内存。

答案 1 :(得分:1)

这个问题的简单答案是'不'。我怀疑你的参考时间比你想象的要长。

您是否正确关闭了这些流?你是intern()那些字符串吗?如果字符串不存在则会导致永久复制由字符串组成,并占用 permgen 空间(未收集)。您正在使用更大字符串的substring()吗?字符串使用flyweight模式,如果使用substring()创建,则将共享字符数组。有关详细信息,请参阅here

您建议垃圾收集未运行。选项-verbose:gc将记录垃圾收集,您可以立即看到正在发生的事情。

答案 2 :(得分:1)

唯一可能导致OutOfMemoryError的字符串就是保留一个更大的字符串的小部分。如果你这样做,那么从堆转储就应该是显而易见的。

当您进行堆转储时,我建议您只查看活动对象,在这种情况下,您不需要的任何保留对象最有可能是代码中的错误。