何时进行垃圾回收?(关于IT技术)

8
我有一段代码,它在内存中加载了一张非常大的图片。因此,调用以下代码似乎是一个合理的选择:
System.gc();

在加载图像之前,从我所知道的来看,它可以正常工作。

昨天,我决定使用一款非常有用的软件叫做FindBugs,它会扫描你的代码并报告可能导致错误或不建议使用的策略。问题是,我提到的这段代码被报告了。描述如下:

... 强制垃圾回收;仅在基准测试代码中极其可疑

并且继续阐述:

代码显式调用垃圾回收。除特定情况外(如基准测试),这是非常可疑的。

过去,人们在close或finalize方法等例程中明确调用垃圾收集器的情况已经导致了巨大的性能黑洞。垃圾回收可能很昂贵。任何强制进行数百或数千次垃圾回收的情况都会使计算机速度变慢。

因此,我的问题是:在这种情况下以编程方式调用垃圾回收器是否不合适?我的代码只调用一次,并且包含它的方法很少使用。如果不允许调用它,那么在需要在进行非常内存密集型操作之前尽可能释放更多内存的情况下,应该怎么做?


1
在这种情况下,您可能正在串行化性能。如果您让GC决定何时运行,它可能会在应用程序在I/O上阻塞时运行。 - Tom Hawtin - tackline
问:何时调用System.gc()?答:永不。 - KitsuneYMG
1
哇,真有眼界。我完全明白为什么不应该根据您的答案调用 gc。非常感谢。 - Savvas Dalkitsis
8个回答

9
通常情况下,垃圾回收器比人类更聪明,所以最好让它在运行时自己决定何时运行。如果运行时需要内存,它会自己运行垃圾回收器。

1
问题在于只有在尝试分配某些内容之后才会出现。如果您处于“知道”自己拥有非常杂乱的内存布局且即将需要大块内存的情况下,可以提前几个指令调用 GC。 - Daniel Goldberg
2
你永远不会确信自己的内存布局是否混乱。垃圾回收器可能已经运行了,这一点你无从得知。即使你不进行任何收集,标记-清除算法中的“标记”部分也需要资源和时间。这就是为什么试图比垃圾回收器更聪明几乎总是一个净负面或(最好的情况)保持平衡的案例。 - Jason
运行时和垃圾回收器可以自由更改和移动内存中的对象,无论何时它感觉需要这样做(如果你来自C语言世界,对象更像指向指针),你几乎永远不会“知道”你有一个混乱的布局。 - nos

9

你通过System.gc()方法获得了性能提升吗? 我认为不会,因为在加载图像之前可能没有需要收集的大量对象。

通常现代垃圾收集器最懂得何时运行,因此除非您有一个非常充分的理由(例如插件所建议的基准测试应用程序),否则不应该强制进行收集。

顺便说一句:调用System.gc()会建议VM执行“完整”或“大型”收集,这意味着所有线程都会短暂停止。 否则它可能只会进行“小”的垃圾回收,不会停止所有线程。

使用-verbose:gc选项运行程序,以查看收集多少字节。

此外,在此处可以找到关于垃圾收集的大量技术信息: http://java.sun.com/developer/technicalArticles/Programming/GCPortal/


4
System.gc()并不会“强制”执行任何操作,虚拟机可以选择忽略它。Javadoc中的措辞是“调用gc方法建议Java虚拟机努力回收未使用的对象”(重点在于“建议”)。 - Adrian Mouat
2
-1 表示你没有仔细阅读 javadoc 上对 System.gc() 的说明,正如 Adrian 所说,它并不能强制执行。要真正强制执行 gc,请参见 http://stackoverflow.com/questions/1147511/how-can-i-estimate-amount-of-memory-left-with-calling-system-gc/1149182#1149182。 - KitsuneYMG

1

调用垃圾回收器是可以的,不会有任何“问题”。 然而,我怀疑它不会显著提高性能,除非该调用还处理了分配数据的碎片整理。我不知道。

在这种情况下,你应该做的是对代码进行分析。运行多次,看看得到什么样的结果。


1
通常情况下,您不应该干涉垃圾回收器的工作。如果在加载图像之前需要释放一些内存,则垃圾回收器会自动处理。
但是,如果只需要执行一次,它可能不会对性能产生重大影响。在循环中执行的操作要重要得多。

1
你已经得到了很多好的建议,我会尽量不重复。如果您确实遇到了GC方面的问题,例如应用程序停止运行一秒钟的情况,请执行以下操作: 1. 检查是否有任何调用System.gc()的方法; 2. 查看各种配置gc的选项。这些选项有很多,并且比强制gc更加有帮助。

1

确保尽早将大对象作为垃圾回收。即将变量设置为空或使其超出范围。这将有所帮助!


1
如果内存分配失败,则会启动GC循环并再次尝试分配。

0

一般来说,不应该调用System.gc()。但是,在某些情况下,这样做是有意义的。

在我编写的软件中,有一个内置的性能跟踪层。它主要用于自动化测试,但也可以用于诊断目的。在测试之间或特定运行之后,我们会调用System.gc几次,然后记录仍然存在的内存。它为我们提供了一个基本的内存占用基准,以便随着时间推移观察内存消耗趋势线。虽然这可以通过一些外部JVM接口完成,但在现场进行操作更容易,并且不需要精确的数字。

在一个非常老的系统上,我们可能有多达72个单独的JVM(是的,72个,在建造时有很好的理由)。在该系统中,让堆在所有72个JVM上自由浮动可能会导致过度的(远远超出物理内存)总内存消耗。在大量数据操作之间调用System.gc(),以尝试使JVM保持接近平均水平,以防止堆增长(限制堆大小可能是另一个方向,但那样就需要实施者在每个站点配置更多内容,并更加了解发生在引擎盖下的情况,以使其正确无误,并且在负载下不会导致系统失败)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接