3

我正在生成一个大型数据结构并将其写入硬盘。之后我想摆脱对象,以减少内存消耗。我的问题是,在我强制进行垃圾回收之后,已使用的内存量至少与垃圾回收之前一样高。我添加了一个最小的工作示例我正在做什么。

DataStructure data = new DateStructure();
data.generateStructure(pathToData);
Writer.writeData(data);
WeakReference<Object> ref = new WeakReference<Object>(data);
data = null;
while (ref.get() != null) {
    System.gc();
}

代码应该按照线程中的建议强制对数据对象进行垃圾收集:
Java 中强制垃圾收集?
我知道这个垃圾收集确实保证了数据对象的删除,但在过去,我通过使用链接中描述的垃圾收集更成功,就像使用简单的 System.gc()。

也许有人有一个答案,什么是摆脱大物体的最佳方法。

4

2 回答 2

7

这似乎是过早的优化(或者更确切地说是一种错觉)。System.gc();不保证强制进行垃圾回收。你在这里做的是忙着等待一些非保证的 gc 发生。但是如果堆没有被填满,JVM 可能根本不会启动垃圾收集。

我认为当你对你的应用程序进行压力测试时,你应该开始考虑这个问题,你可以将这部分识别为瓶颈。

所以简而言之,你不能真正强制gc,这是故意的。JVM 将知道何时以及如何释放空间。我认为,如果您清除参考资料并打电话System.gc();,您可以继续前进,而无需关心它是否被清理。您可以阅读有关如何微调垃圾收集器的官方文档。您应该根据文档使用一些 GC 调整,而不是要求 java 从您的代码中进行 GC。

只是一个旁注:如果需要,JVM 将扩展堆的一些代。据我所知,有一个配置选项,您可以在其中设置 JVM 何时收缩一代的百分比。MinHeapFreeRatio/MaxHeapFreeRatio如果您不希望 Java 保留它不需要的内存,请使用。

于 2013-06-20T14:17:43.523 回答
2

这个成语被打破的原因有很多,这里有一些:

  1. System.gc()不强迫任何事情;这只是对垃圾收集器的提示;
  2. 无法保证何时清除弱引用。规范说“假设垃圾收集器在某个时间点确定对象是弱可访问的”。发生这种情况时,取决于实施;
  3. 即使在弱引用被清除之后,也不知道它的引用对象的内存何时会真正被回收。那时您唯一知道的是该对象已从“弱可达”转变为“可终结”。它甚至可能从终结器中复活。

根据我的经验,只需执行System.gc固定次数,例如三次,它们之间的延迟(您的 GC 可能是 ConcurrentMarkSweep)在半秒到秒的范围内,比这些所谓的“智能”方法提供更稳定的结果。

最后一点:永远不要System.gc在生产代码中使用。几乎不可能让它为您的项目带来任何价值。我仅将它用于微基准测试。

更新

在评论中,您提供了一条不在您的问题中的关键信息:您有兴趣在完成对象后减少总堆大小( Runtime#totalMemory),而不仅仅是堆占用 ( Runtime#totalMemory-Runtime#freeMemory)。这完全在程序控制之外,并且在某些 JVM 实现中它永远不会发生:一旦堆增加,内存就永远不会释放回操作系统。

于 2013-06-20T14:30:18.433 回答