在回答了一个关于如何使用System.gc()在Java中强制释放对象的问题(这个人正在清除1.5GB的HashMap)后,我被告知手动调用System.gc()是一种糟糕的做法,但评论并不完全令人信服。此外,似乎没有人敢对我的回答投赞成票,也没有人敢投反对票。

我在那里被告知这是一种糟糕的做法,但后来我又被告知垃圾收集器的运行不再系统地停止整个世界,而且JVM也只能有效地将其用作提示,所以我有点不知所措。

我知道JVM通常比您更了解何时需要回收内存。我也明白,担心几千字节的数据是愚蠢的。我也明白,即使是兆字节的数据也不如几年前了。但还是1.5 gb ?你知道大概有1.5 GB的数据在内存中;这又不是瞎猜的。System.gc()在系统上是坏的,还是在某个点上变得正常了?

所以这个问题实际上是双重的

Why is or isn't it bad practice to call System.gc()? Is it really merely a hint to the JVM under certain implementations, or is it always a full collection cycle? Are there really garbage collector implementations that can do their work without stopping the world? Please shed some light over the various assertions people have made in the comments to my answer. Where's the threshold? Is it never a good idea to call System.gc(), or are there times when it's acceptable? If so, what are those times?


当前回答

Since objects are dynamically allocated by using the new operator, you might be wondering how such objects are destroyed and their memory released for later reallocation. In some languages, such as C++, dynamically allocated objects must be manually released by use of a delete operator. Java takes a different approach; it handles deallocation for you automatically. The technique that accomplishes this is called garbage collection. It works like this: when no references to an object exist, that object is assumed to be no longer needed, and the memory occupied by the object can be reclaimed. There is no explicit need to destroy objects as in C++. Garbage collection only occurs sporadically (if at all) during the execution of your program. It will not occur simply because one or more objects exist that are no longer used. Furthermore, different Java run-time implementations will take varying approaches to garbage collection, but for the most part, you should not have to think about it while writing your programs.

其他回答

人们已经很好地解释了为什么不使用它,所以我将告诉你一些你应该使用它的情况:

(下面的评论适用于在带有CMS收集器的Linux上运行的Hotspot,在这里我有信心地说System.gc()实际上总是调用完整的垃圾收集)。

After the initial work of starting up your application, you may be a terrible state of memory usage. Half your tenured generation could be full of garbage, meaning that you are that much closer to your first CMS. In applications where that matters, it is not a bad idea to call System.gc() to "reset" your heap to the starting state of live data. Along the same lines as #1, if you monitor your heap usage closely, you want to have an accurate reading of what your baseline memory usage is. If the first 2 minutes of your application's uptime is all initialization, your data is going to be messed up unless you force (ahem... "suggest") the full gc up front. You may have an application that is designed to never promote anything to the tenured generation while it is running. But maybe you need to initialize some data up-front that is not-so-huge as to automatically get moved to the tenured generation. Unless you call System.gc() after everything is set up, your data could sit in the new generation until the time comes for it to get promoted. All of a sudden your super-duper low-latency, low-GC application gets hit with a HUGE (relatively speaking, of course) latency penalty for promoting those objects during normal operations. It is sometimes useful to have a System.gc call available in a production application for verifying the existence of a memory leak. If you know that the set of live data at time X should exist in a certain ratio to the set of live data at time Y, then it could be useful to call System.gc() a time X and time Y and compare memory usage.

首先,规范和现实之间是有区别的。规范说System.gc()提示GC应该运行,VM可以忽略它。实际情况是,VM永远不会忽略对System.gc()的调用。

Calling GC comes with a non-trivial overhead to the call and if you do this at some random point in time it's likely you'll see no reward for your efforts. On the other hand, a naturally triggered collection is very likely to recoup the costs of the call. If you have information that indicates that a GC should be run than you can make the call to System.gc() and you should see benefits. However, it's my experience that this happens only in a few edge cases as it's very unlikely that you'll have enough information to understand if and when System.gc() should be called.

这里列出了一个例子,在IDE中敲击垃圾桶。如果你要去开会,为什么不去呢?开销不会影响您,当您返回时,可能会清理堆。在生产系统中执行此操作,频繁调用收集将使其彻底停止!即使是RMI偶尔发出的调用也会对性能造成破坏。

根据我的经验,使用System.gc()实际上是一种平台特定形式的优化(其中“平台”是硬件架构、OS、JVM版本和可能的更多运行时参数(如可用的RAM)的组合),因为它的行为虽然在特定平台上大致可预测,但在不同平台之间可能(也将)有很大差异。

是的,在某些情况下System.gc()将提高(可感知的)性能。举个例子,如果延迟在你的应用的某些部分是可以容忍的,但在其他部分却不能(就像上文所提到的游戏例子,你希望GC发生在关卡开始时,而不是在关卡进行时)。

然而,它是帮助还是伤害(或什么都不做)在很大程度上取决于平台(如上所定义)。

所以我认为这是针对特定平台的最后一种优化方法(即如果其他性能优化还不够的话)。但是,您绝不应该仅仅因为相信它可能有帮助(没有特定的基准)就调用它,因为它很可能没有帮助。

每个人总是说要避免System.gc()的原因是,它是一个很好的指示器,显示出从根本上坏掉的代码。任何依赖于它的正确性的代码肯定是坏的;任何依赖于它的性能都很可能是坏的。

您不知道您正在哪种垃圾收集器下运行。当然,有一些jvm并没有像您断言的那样“停止世界”,但是有些jvm并没有那么聪明,或者由于各种原因(也许它们在电话上?)没有做到这一点。你不知道它会做什么。

而且,它不能保证做任何事情。JVM可能会完全忽略您的请求。

“你不知道它会做什么”,“你甚至不知道它是否有用”,以及“你无论如何都不需要调用它”,这就是为什么人们如此强烈地说,一般来说你不应该调用它。我认为这是一个“如果你需要问你是否应该使用这个,你不应该”的案例


EDIT来解决其他线程的一些问题:

在阅读了你链接的帖子后,还有一些事情我想指出来。 首先,有人建议调用gc()可能会向系统返回内存。这当然不一定是正确的——Java堆本身的增长独立于Java分配。

例如,JVM将保留内存(几十兆字节),并根据需要增加堆。即使在释放Java对象时,它也不一定会将内存返回给系统;保留已分配的内存以供将来的Java分配使用是完全自由的。

为了显示System.gc()可能什么也不做,请查看 JDK bug 6668279 特别是有一个-XX:DisableExplicitGC VM选项:

默认情况下,对System.gc()的调用是启用的(-XX:-DisableExplicitGC)。使用-XX:+DisableExplicitGC禁用对System.gc()的调用。请注意,JVM在必要时仍然执行垃圾收集。

也许我写的代码很糟糕,但我已经意识到在eclipse和netbeans ide上点击垃圾桶图标是一个“好的实践”。