在回答了一个关于如何使用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?


当前回答

GC效率依赖于许多启发式方法。例如,一个常见的启发是,对对象的写访问通常发生在不久前创建的对象上。另一个原因是许多对象的寿命非常短(有些对象会使用很长时间,但许多对象在创建后几微秒就会被丢弃)。

调用System.gc()就像踢掉GC。它的意思是:“所有那些精心调整的参数,那些聪明的组织,所有你投入到分配和管理对象上的努力,让事情顺利进行,好吧,放弃所有这些,从头开始”。它可以提高性能,但大多数时候它只会降低性能。

要可靠地(*)使用System.gc(),您需要了解GC的所有细节。如果使用其他供应商的JVM,或者使用同一供应商的下一个版本,或者使用相同JVM但命令行选项略有不同,那么这些细节可能会发生很大变化。因此,这很少是一个好主意,除非你想解决一个你控制所有这些参数的特定问题。因此就有了“坏做法”的概念:这并没有被禁止,方法是存在的,但它很少有回报。

我在这里谈论的是效率。System.gc()永远不会破坏正确的Java程序。它既不会产生JVM无法获得的额外内存:在抛出OutOfMemoryError之前,JVM会执行System.gc()的工作,即使是作为最后的手段。

其他回答

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

这是一个非常麻烦的问题,我觉得这是许多人反对Java的原因,尽管它是一种多么有用的语言。

你不能相信"系统"gc”来做任何事情都令人难以置信地令人生畏,并且很容易调用“恐惧,不确定,怀疑”的语言感觉。

在许多情况下,在重要事件发生之前处理您故意引起的内存峰值是很好的,这将导致用户认为您的程序设计很糟糕/反应迟钝。

拥有控制垃圾收集的能力将是一个非常好的教育工具,进而提高人们对垃圾收集如何工作以及如何使程序利用其默认行为和受控行为的理解。

让我回顾一下这篇文章的论点。

效率低下:

通常情况下,程序可能什么都不做,而您知道它什么都不做是因为它的设计方式。例如,它可能正在使用一个大的等待消息框进行某种长时间的等待,最后它可能会添加一个调用来收集垃圾,因为运行它的时间只占长等待时间的一小部分,但可以避免gc在更重要的操作中间发生故障。

这是一种不好的做法,表明代码有问题。

我不同意,不管你有什么垃圾收集器。它的工作是追踪垃圾并清理垃圾。

通过在使用不那么关键的时候调用gc,当您的生命依赖于正在运行的特定代码,但它却决定收集垃圾时,您可以减少gc运行的几率。

当然,它的行为可能不是您想要或期望的方式,但当您确实想要调用它时,您知道什么都没有发生,并且用户愿意容忍缓慢/停机。如果系统。Gc工作,太棒了!如果没有,至少你试过了。没有任何缺点,除非垃圾收集器具有固有的副作用,会对手动调用垃圾收集器的行为产生可怕的意想不到的影响,而这本身就会引起不信任。

这不是一个常见的用例:

这是一个不能可靠地实现的用例,但如果系统以这种方式设计,则可以实现。这就像做一个交通灯,让它的一些/所有的交通灯的按钮不做任何事情,这让你质疑为什么按钮在那里开始,javascript没有垃圾收集功能,所以我们没有仔细检查它。

规范说System.gc()提示GC应该运行,VM可以忽略它。

什么是“暗示”?什么是“忽略”?计算机不能简单地接受暗示或忽略某些东西,它所采取的严格行为路径可能是动态的,由系统的意图指导。一个正确的答案应该包括垃圾收集器在实现级别上实际做了什么,导致它在您请求它时不执行收集。这个功能只是一个nop吗?有什么条件是必须满足的吗?这些条件是什么?

就目前的情况而言,Java的GC通常看起来像一个不值得信任的怪物。你不知道它什么时候来,什么时候走,你不知道它会做什么,它会怎么做。我可以想象一些专家对他们的垃圾收集如何在每条指令的基础上工作有更好的想法,但绝大多数人只是希望它“只是工作”,不得不相信一个看起来不透明的算法为你工作是令人沮丧的。

阅读一些东西或学习一些东西,与实际看到它的实现,不同系统之间的差异,以及能够在不查看源代码的情况下使用它之间有很大的差距。这会创造自信和掌控/理解/控制的感觉。

总而言之,“这个功能可能不会做任何事情,我不会详细说明它什么时候会做什么事情,什么时候不会做,为什么不会或会做,这通常意味着尝试这样做是违反哲学的,即使背后的意图是合理的”,这是一个固有的问题。

It might be okay for Java GC to behave the way it does, or it might not, but to understand it, it is difficult to truly follow in which direction to go to get a comprehensive overview of what you can trust the GC to do and not to do, so it's too easy simply distrust the language, because the purpose of a language is to have controlled behavior up to philosophical extent(it's easy for a programmer, especially novices to fall into existential crisis from certain system/language behaviors) you are capable of tolerating(and if you can't, you just won't use the language until you have to), and more things you can't control for no known reason why you can't control them is inherently harmful.

GC效率依赖于许多启发式方法。例如,一个常见的启发是,对对象的写访问通常发生在不久前创建的对象上。另一个原因是许多对象的寿命非常短(有些对象会使用很长时间,但许多对象在创建后几微秒就会被丢弃)。

调用System.gc()就像踢掉GC。它的意思是:“所有那些精心调整的参数,那些聪明的组织,所有你投入到分配和管理对象上的努力,让事情顺利进行,好吧,放弃所有这些,从头开始”。它可以提高性能,但大多数时候它只会降低性能。

要可靠地(*)使用System.gc(),您需要了解GC的所有细节。如果使用其他供应商的JVM,或者使用同一供应商的下一个版本,或者使用相同JVM但命令行选项略有不同,那么这些细节可能会发生很大变化。因此,这很少是一个好主意,除非你想解决一个你控制所有这些参数的特定问题。因此就有了“坏做法”的概念:这并没有被禁止,方法是存在的,但它很少有回报。

我在这里谈论的是效率。System.gc()永远不会破坏正确的Java程序。它既不会产生JVM无法获得的额外内存:在抛出OutOfMemoryError之前,JVM会执行System.gc()的工作,即使是作为最后的手段。

很多人似乎都告诉你不要这样做。我不同意。如果在加载关卡等大型加载过程后,你认为:

您有很多不可访问的对象,可能还没有被gc - ed。而且 您认为此时用户可以忍受轻微的减速

调用System.gc()没有害处。我把它看作c/c++的内联关键字。这只是对gc的一个提示,即您(开发人员)已经决定时间/性能不像通常那样重要,其中一些时间/性能可以用于回收内存。

建议不要依赖它做任何事情是正确的。不要依赖于它的工作,但给一个提示,现在是一个可以接受的时间收集是完全可以的。我宁愿把时间浪费在代码中无关紧要的地方(加载屏幕),也不愿浪费在用户与程序积极互动的时候(比如在游戏关卡中)。

有一次,我将强制收集:当试图找出是一个特定的对象泄漏(本机代码或大型,复杂的回调交互。哦,还有任何UI组件,哪怕只是瞥了一眼Matlab。)在产品代码中不应该使用这种方法。

每个人总是说要避免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在必要时仍然执行垃圾收集。