我正在生成一个大型数据结构并将其写入硬盘。之后我想摆脱对象,减少内存消耗。我的问题是,在我强制进行垃圾收集后,使用的内存量至少与垃圾收集前一样高。我添加了一个最小的工作示例,我正在做什么。
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()。
也许某人有一个答案,那就是摆脱大型物品的最好方法。
答案 0 :(得分:7)
这似乎是过早的优化(或者更确切地说是幻觉)。 System.gc();
无法保证强制进行垃圾回收。你在这里做的是忙着等待一些无保证的gc发生。但是如果堆没有被填满,JVM可能根本不会启动垃圾收集。
我认为在对应用程序进行压力测试时应该开始考虑这个问题,并且可以将此部分识别为瓶颈。
所以简而言之,你不能强制一个gc,这是故意的。 JVM将知道何时以及如何释放空间。我认为,如果你清除你的引用并致电System.gc();
,你可以继续前进而不关心它是否得到清理。您可以阅读有关如何微调垃圾收集器的Official documentation。您应该根据文档使用某些GC调优,而不是从代码中询问java到GC。
只是一个旁注:如果需要,JVM将扩展一些堆的代。据我所知,有一个配置选项,您可以在JVM收缩一代时设置一些百分比。如果您不希望Java保留不需要的内存,请使用MinHeapFreeRatio/MaxHeapFreeRatio
。
答案 1 :(得分:2)
这个成语因各种原因而被打破,这里有一些:
System.gc()
不强迫任何东西;它只是垃圾收集器的提示; 根据我的经验,只需执行System.gc
固定次数,例如三次,它们之间有延迟(你的GC可能是ConcurrentMarkSweep),在半秒到秒的范围内,给出的结果比这些所谓的“聪明”方法。
最后一点:永远不要在生产代码中使用System.gc
。几乎不可能让它为您的项目带来任何价值。我只将它用于微基准测试。
在评论中,您提供了一个不在您问题中的关键信息:在完成对象后,您有兴趣减少总堆大小(Runtime#totalMemory
) ,而不仅仅是堆占用率(Runtime#totalMemory-Runtime#freeMemory
)。这完全不受编程控制的影响,并且在某些JVM实现上它永远不会发生:一旦堆增加,内存永远不会释放回操作系统。