Java - 为什么强制垃圾收集不释放内存

时间:2013-06-20 14:08:48

标签: java memory garbage-collection

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

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()。

也许某人有一个答案,那就是摆脱大型物品的最好方法。

2 个答案:

答案 0 :(得分:7)

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

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

所以简而言之,你不能强制一个gc,这是故意的。 JVM将知道何时以及如何释放空间。我认为,如果你清除你的引用并致电System.gc();,你可以继续前进而不关心它是否得到清理。您可以阅读有关如何微调垃圾收集器的Official documentation。您应该根据文档使用某些GC调优,而不是从代码中询问java到GC。

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

答案 1 :(得分:2)

这个成语因各种原因而被打破,这里有一些:

  1. System.gc()不强迫任何东西;它只是垃圾收集器的提示;
  2. 无法保证何时清除弱引用。规范说“假设垃圾收集器在某个时间点确定一个对象是弱可达的”。当发生这种情况时,由实施决定;
  3. 即使在弱引用被清除之后,也无法确定它的指示物的内存何时会被回收。在这一点上你唯一知道的是对象已经从“弱可达”转变为“可终结”。它甚至可以从终结者身上复活。
  4. 根据我的经验,只需执行System.gc固定次数,例如三次,它们之间有延迟(你的GC可能是ConcurrentMarkSweep),在半秒到秒的范围内,给出的结果比这些所谓的“聪明”方法。

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

    更新

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