【问题标题】:Java - Why forced garbage collection doesn't release memoryJava - 为什么强制垃圾回收不释放内存
【发布时间】:2013-06-20 14:08:47
【问题描述】:

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

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();
}

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

也许有人知道什么是摆脱大物体的最佳方法。

【问题讨论】:

  • 取决于你需要多长时间释放内存,如果 gc 之前的可用空间不够,我不会强制它,而是简单地将引用清空,让 gc 在下一次滴答时完成它的工作。
  • "已用内存量至少与垃圾回收前一样高" 你是如何衡量的?您确定这是已用内存而不是最大允许内存吗?
  • 我可以在系统监视器中看到已使用的内存量,它应该从几乎满减少到几乎空。其次,我使用 runtime.totalMemory() - runtime.freeMemory() 来获取实际数量。这表明堆增加了。

标签: java memory garbage-collection


【解决方案1】:

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

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

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

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

【讨论】:

  • 获取更多详细信息。堆可能没有被填满 - 这是真的。你的回答给我带来了更大的麻烦。我正在使用一个相当大的交换分区,数据结构使用它的一部分。使用 SSD,如果临时使用它是可以的,但并非一直都会减慢整台机器的速度。这就是为什么即使堆未满我也不想等待垃圾回收的原因。
  • 您可以使用不同的 GC 策略,就像 @Marko Topolnik 指出的那样。
【解决方案2】:

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

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

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

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

更新

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

【讨论】:

    猜你喜欢
    • 2015-11-16
    • 1970-01-01
    • 1970-01-01
    • 2019-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-26
    相关资源
    最近更新 更多