8

我有一段代码可以在内存中加载一个非常大的图像。所以这似乎是一个合理的事情

System.gc();

在加载图像之前。据我所知,它没有问题。

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

...强制垃圾收集;非常可疑,除非在基准测试代码中

它继续详细说明:

代码显式调用垃圾收集。除了在基准测试中的特定用途外,这是非常可疑的。

过去,人们在 close 或 finalize 方法等例程中显式调用垃圾收集器的情况导致了巨大的性能黑洞。垃圾收集可能很昂贵。任何强制进行成百上千次垃圾收集的情况都会使机器爬行。

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

4

8 回答 8

9

通常 GC 比你更聪明,所以最好让它在运行时决定时运行。如果运行时需要内存,它将自行运行 GC

于 2009-07-18T12:17:19.730 回答
9

您是否通过 System.gc() 获得了任何性能改进?我不这么认为,因为在加载图像之前您可能没有很多需要收集的对象。

通常现代垃圾收集器最清楚何时运行,所以你不应该强制收集,除非你有一个非常好的理由。(例如该插件建议的基准测试应用程序)

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

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

这里还有很多关于垃圾收集的技术信息:http: //java.sun.com/developer/technicalArticles/Programming/GCPortal/

于 2009-07-18T12:18:12.833 回答
1

你已经得到了很多好的建议,我将尽量不重复。

如果您确实遇到了 GC 问题,例如您的应用程序完全停止了一秒钟,请执行以下操作: 1. 检查是否有任何对 System.gc() 的调用;2. 查看配置 gc 的各种选项。周围有很多人,他们更有帮助,然后强制gc。

于 2009-07-18T12:30:16.347 回答
1

确保可以尽早 gc'ed 大对象。即将变量设置为 null 和/或让它们超出范围。这有帮助!

于 2009-07-18T12:31:06.223 回答
1

调用垃圾收集器很好,你不会从中得到任何“问题”。但是,我怀疑它是否会显着提高性能,除非该调用还涉及对分配的数据进行碎片整理。我不知道。

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

于 2009-07-18T12:14:55.970 回答
1

通常,您不应干扰垃圾收集器。如果在加载图像之前需要释放一些内存,那么垃圾收集器会为您完成。

无论如何,如果你只做一次,它可能不会严重影响你的表现。在循环中完成的事情要重要得多。

于 2009-07-18T12:17:38.373 回答
1

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

于 2009-07-18T17:36:55.037 回答
0

一般来说,没有。调用 System.gc() 是不合适的。但是,我遇到过一些有意义的案例。

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

在一个更老的系统上,我们可能有超过 72 个独立的 JVM(是的,72 个,在构建时有一个很好的理由)。在那个系统中,让堆在所有 72 个 JVM 上自由浮动可能会产生一些过多的(远远超出物理内存)总内存消耗。System.gc() 在大量数据练习之间被调用,以尝试使 JVM 更接近平均水平,以防止堆增长(封顶堆可能是另一个方向,但它会要求实现者为每个站点配置更多,并且更加了解引擎盖下发生的事情,以使其正确并且不让系统在负载下失败)。

于 2009-07-18T14:21:07.050 回答