为什么调用System.gc()是不良实践?

362

回答有关如何在Java中强制释放对象(该人正在清除一个1.5GB的HashMap)使用System.gc()的问题后,我被告知手动调用System.gc()是不良实践,但评论并没有完全说服我。此外,没有人似乎敢于点赞或踩我的答案。

我被告知那里这是不良实践,但我也被告知垃圾收集器运行不再系统性地停止世界,并且JVM也可以有效地将其仅用作提示,所以我有点迷茫。

我确实理解JVM通常比您更了解何时需要回收内存。我也理解担心几千字节的数据是愚蠢的。我也明白即使是几兆字节的数据也不是几年前的那样了。但是,1.5 GB?并且你知道有大约1.5GB的数据挂在内存中,这不像是一次尝试。 System.gc()是否系统性不好,或者在某个点上变得可以接受?

因此问题实际上是双重的:

  • 调用System.gc()是好还是不好的做法?在某些情况下它只是对JVM的提示,还是总是会进行完整的垃圾回收循环?是否真的有垃圾收集器实现可以在不停止程序的情况下完成其工作?请阅读answer评论中人们所发表的各种断言,让我们来探讨一下。
  • 问题在于什么时候适当地使用System.gc()。是否永远不应该调用它,或者在何时可以接受它?如果可以,请说明那些情况是什么。

4
我认为,在进行一些长时间加载的过程中,调用System.gc()是个不错的时机。例如,我正在制作一款游戏,并计划在游戏加载新关卡时,在加载过程结束时调用System.gc()。用户已经等待了一段时间,额外的性能提升可能是值得的;但我也会在配置屏幕上放置一个选项来禁用这种行为。 - Ricket
1
另一个案例已经在http://java-monitor.com/forum/showthread.php?t=188中解释了,其中解释了调用System.gc()可能是不良实践的情况。 - Shirishkumar Bari
在阅读了一些答案后,我想发表一下自己的看法(需要注意的是,我对Java还比较新,所以请谨慎考虑我的意见),但是...我认为更好的问题可能是“为什么你首先要有这么大的HashMap?” - Twisted Code
13个回答

257
大家总是建议避免调用 System.gc() 是因为它很可能是代码基本上出了问题的一个很好的指标。任何依赖它确保正确性的代码肯定是有问题的;任何依赖它来提高性能的代码大概率也有问题。
你不知道你正在运行什么类型的垃圾收集器。确实有一些收集器不会像你所断言的那样“停止整个世界”,但某些JVM可能并不那么聪明,或者由于各种原因(例如它们在手机上?)不这样做。你不知道它会做什么。
而且,并不能保证它会做任何事情。JVM可能完全忽略你的请求。
“你不知道它会做什么”,“你不知道它是否有帮助”,以及“你本来就不应该调用它”这些因素就是人们坚定地表示通常不应该调用它的原因。我认为这是一个“如果你需要问是否应该使用它,你就不应该使用它”的例子。
编辑以解决其他线程中的一些问题:
阅读您链接的线程后,我想指出几件事情。首先,有人建议调用 gc() 可以将内存返回给系统。那肯定不一定是真的- Java堆本身独立于Java分配而增长。
也就是说,JVM将持有内存(许多十兆字节)并根据需要增加堆。即使您释放Java对象,它也未必会将该内存返回给系统;它完全有权保留已分配的内存以供未来的Java分配使用。
要展示可能 System.gc() 什么也不做,请查看 JDK bug 6668279 。特别是,有一个-XX:DisableExplicitGC虚拟机选项:

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


1
你可能能够构建一些奇怪的Rube Goldberg式设置,其中GC运行的方式会影响您代码的正确性。也许它掩盖了一些奇怪的线程交互,或者最终器对程序的运行有重大影响。我不完全确定这是可能的,但可能性存在,所以我想提一下。 - Steven Schlansker
2
@zneak 例如,您可能已将关键代码放在终结器中(这是根本上有问题的代码)。 - Martin
3
我想补充一下,有一些极端情况下System.gc()是有用的,甚至可能是必需的。例如,在Windows上的UI应用程序中,如果在最小化窗口之前调用System.gc(),可以极大地加快恢复窗口的过程(特别是当它保持最小化状态相当长时间并且进程的某些部分被交换到磁盘上时)。 - Joachim Sauer
2
@AndrewJanke 我会说,对于想要保留的对象使用WeakReference的代码从一开始就是不正确的,无论是否进行垃圾回收。在C++中使用std::weak_ptr也会遇到同样的问题(尽管您可能会比Java版本更早地注意到该问题,因为对象销毁不像最终化通常那样被延迟)。 - JAB
2
@rebeccah 那是一个bug,所以我会称之为“肯定是坏的”。System.gc()可以解决这个问题,但这只是一种解决方法,而不是良好的编程实践。 - Steven Schlansker
显示剩余8条评论

152

已经解释过了,调用system.gc()可能不会产生任何作用,并且任何需要垃圾收集器运行的代码都是有问题的。

然而,不建议调用System.gc()的实际原因是它效率低下。在最坏的情况下,它甚至是极其低效的!让我来解释一下。

一个典型的垃圾收集算法通过遍历堆中所有非垃圾对象并推断未被访问的任何对象必须是垃圾来识别垃圾。从这个角度来看,我们可以将垃圾收集的总工作量建模为一个部分与存活数据量成正比,另一个部分与垃圾数量成正比;即 work = (live * W1 + garbage * W2)

现在假设您在单线程应用程序中执行以下操作:

System.gc(); System.gc();
第一次调用将(我们预测)执行(活跃对象数 * W1 + 垃圾对象数 * W2)的工作,并清除未使用的垃圾。
第二次调用将执行(活跃对象数 * W1 + 0 * W2)的工作,什么也不回收。换句话说,我们做了(活跃对象数 * W1)的工作,却一事无成
我们可以将收集器的效率建模为收集一个单位垃圾所需的工作量;即efficiency = (live * W1 + garbage * W2) / garbage。因此,要使GC尽可能高效,我们需要在运行GC时最大化garbage的值;即等待堆内存填满。(同时,还要尽可能扩大堆大小。但这是另一个话题。)
如果应用程序不干扰(通过调用System.gc()),则GC将等待堆内存填满后再运行,从而高效地收集垃圾1。但如果应用程序强制进行GC,则堆内存很可能没有填满,结果将是垃圾收集效率低下。应用程序越频繁地强制进行GC,GC的效率就越低。
注意:上述解释略过了一个事实,即典型的现代GC将堆内存分成“空间”,GC可能动态扩展堆,非垃圾对象的工作集可能会变化等等。即使如此,相同的基本原则适用于所有真正的垃圾收集器2。强制运行GC是低效的。

1-这就是“吞吐量”收集器的工作方式。并发收集器(如CMS和G1)使用不同的标准决定何时启动垃圾收集器。

2-我也排除了仅使用引用计数的内存管理器,但是当前的Java实现没有采用这种方法……有很好的理由。


47
+1 很好的解释。但需要注意的是,这种推理仅适用于您关心吞吐量的情况。如果您想在特定点优化延迟,强制进行垃圾回收可能是有意义的。例如(假设)在游戏中,您可能希望避免在关卡期间出现延迟,但您不在乎在加载关卡时的延迟。然后,在关卡加载后强制进行垃圾回收就是有意义的。这确实会降低总吞吐量,但这不是您要优化的内容。 - sleske
3
@sleske - 你所说的是对的。然而,在各层之间运行GC只是一种临时解决方案...如果各层需要花费足够长的时间,以至于您仍需要在某个层级期间运行GC,则无法解决延迟问题。更好的方法是使用并发(低暂停时间)垃圾收集器...如果平台支持的话。 - Stephen C
不太确定您所说的“需要垃圾回收器运行”的意思。应用程序需要避免的条件是:无法满足的分配失败和高GC开销。随机调用System.gc()通常会导致高GC开销。 - Kirk
@Kirk - “随机调用 System.gc() 通常会导致较高的 GC 开销。”我知道。任何阅读并理解了我的答案的人都应该知道。 - Stephen C
“我正在努力思考我应该在哪里尝试这个!” - 不幸的是,有些人确实会尝试这样的事情。 - Stephen C
显示剩余2条评论

51
很多人似乎告诉你不要这样做。我不同意。如果在像加载关卡这样的大型加载过程之后,您认为:
  1. 您有很多无法访问且可能没有被垃圾回收的对象;并且
  2. 您认为用户可以忍受此时的轻微减速。
调用System.gc()没有任何害处。我认为它就像c / c ++中的inline关键字。这只是一个提示,告诉gc您作为开发人员已经决定时间/性能并不像通常那样重要,某些时间可以用于回收内存。
不依赖它做任何事情的建议是正确的。不要指望它起作用,但是给出提示,现在是收集的可接受时间是完全可以的。我宁愿在代码中无关紧要的地方(加载屏幕)浪费时间,也不愿在用户积极与程序交互时浪费时间(例如在游戏的某个关卡期间)。
只有一种情况我会强制进行垃圾回收:当尝试查找特定对象是否泄漏时(无论是本机代码还是大型复杂回调交互。哦,以及任何轻微瞥一眼Matlab的UI组件)。这绝不能在生产代码中使用。

4
在分析内存泄漏时,为垃圾收集加上+1。请注意,有关堆使用情况(例如Runtime.freeMemory())只有在强制进行垃圾收集后才真正有意义,否则它将取决于系统上一次运行垃圾收集的时间。 - sleske
4
调用System.gc()并无害处,但可能需要采用“停止全世界”的方式,如果发生这种情况,将会带来实际的伤害。 - bestsss
7
显式调用垃圾收集器是有害的。在错误的时间调用垃圾收集器会浪费CPU周期。作为程序员,你没有足够的信息来确定何时是正确的时间...但JVM有这些信息。 - Stephen C
无论是“停止世界”垃圾回收还是并发垃圾回收或其他什么,Hotspot JVM都没有100%的并行垃圾回收。因此,无论你如何调整垃圾回收,停止世界都会在某个时候发生(最好可以推迟它)。因此,我认为有些情况下您不希望进行垃圾回收循环。因此,我认为最好在性能不重要的情况下事先进行垃圾回收。 - Harindu Dilshan
@HarinduDilshan - 你混淆了“停止全世界”和“full gc”。(以及并行)你是正确的,所有HotSpot GC都在某个时候停止整个虚拟机,例如执行新生代垃圾回收(即使使用低暂停收集器)。但是,full GC不同,它通常在整个堆收集时导致停止整个虚拟机。这就是为什么人们对此感到担忧的原因。 - Stephen C
显示剩余3条评论

36

有人已经很好地解释了为什么不使用它,所以我将告诉您几种应该使用它的情况:

(以下评论适用于在Linux上运行CMS收集器的Hotspot,在这里我可以自信地说System.gc()实际上总会调用完全垃圾回收)。

  1. 在初始化应用程序后的工作中,您可能处于内存使用的非常糟糕的状态。您的一半老年代可能充满垃圾,这意味着您距离第一个CMS只有更近了。对于那些重要的应用程序来说,调用System.gc()“重置”堆到活动数据的起始状态并不是个坏主意。

  2. 与#1类似,如果您密切监视堆使用情况,您需要准确地读取基准内存使用情况。如果您应用程序前2分钟都是初始化,则除非您提前强制(啊咳咳... “建议”)进行完整垃圾回收,否则您的数据就会混乱。

  3. 您可能有一个设计在运行时永远不会将任何东西推广到老年代的应用程序。但也许您需要初始化一些数据,这些数据不太大,无法自动转移到老年代。除非您在设置所有内容后调用System.gc(),否则您的数据可能会停留在新生代,直到它们被推广的时候。突然间,您超级无故障,低GC应用程序会因为在正常操作期间推广这些对象而遭受巨大(相对而言,当然)的延迟惩罚。

  4. 在生产应用程序中,有时具有System.gc调用可用于验证内存泄漏的存在是有用的。如果您知道时间X的活动数据集应该与时间Y的活动数据集保持一定比例,则在时间X和时间Y调用System.gc()并比较内存使用情况可能很有用。


1
对于大多数分代垃圾收集器,新生代中的对象必须经过一定数量(通常是可配置的)的垃圾收集才能存活,因此调用System.gc()一次来强制升级对象并没有任何好处。而且你肯定不想连续八次调用System.gc(),然后祈祷现在已经完成了升级,并且延迟升级所节省的成本可以弥补多次完整GC的成本。根据GC算法的不同,提升大量对象甚至可能不会产生实际成本,因为它只会将内存重新分配给老年代或并发复制... - Holger

14

这是一个非常令人烦恼的问题,我认为它使许多人反对Java,尽管Java是一种非常有用的语言。

"System.gc"不可靠的事实是令人难以置信的,它很容易给该语言带来“恐惧、不确定性、怀疑”的感觉。

在许多情况下,在重要事件发生之前,有能力处理您故意引起的内存消耗峰值是件好事,否则用户会认为您的程序设计不良/无响应。

控制垃圾收集将成为非常好的教育工具,进而提高人们对垃圾收集机制的理解,并学会如何利用其默认行为和受控行为来编写程序。

让我回顾一下这个线程的论点。

  1. 它效率低:

通常,程序可能什么都没做,因为它是按照设计方式运行的。例如,它可能正在进行某种长时间等待,并且在结束时可能会添加一个调用以收集垃圾,因为运行时间将占长时间等待的极小部分时间,但它可以避免gc在更重要的操作中出现问题。

  1. 它总是一种糟糕的做法,并且表示代码有问题。

我不同意,无论您使用什么垃圾收集器,它的工作都是跟踪垃圾并清除它。

通过在使用较不关键的时间调用gc,您可以减少它在您的生命取决于特定代码运行时运行但决定收集垃圾的可能性。

当然,它可能不会按照您想要或期望的方式运行,但当您想要调用它时,您知道没有任何事情正在发生,用户愿意容忍缓慢/停机时间。如果System.gc有效,太好了!如果不行,至少你尝试过。除非垃圾收集器具有固有的副作用,这些副作用会对手动调用垃圾收集器的行为产生可怕而意外的影响,否则就没有任何负面影响,这本身引起了不信任。

  1. 它不是常见用例:

这是一个无法可靠实现的用例,但如果系统被设计成那样,就可以实现。就像制造交通信号灯并使其中一些或全部的按钮不起作用一样,这会让您怀疑为什么会有这个按钮,Javascript没有垃圾回收功能,因此我们对它不会过分挑剔。

  1. 规范说明System.gc()是提示GC运行的指令,虚拟机可以忽略它。

"提示"是什么?"忽略"是什么?计算机不能简单地接受提示或忽略某些东西,它们采取的行为路径可能是动态的,并由系统意图指导。一个正确的答案应该包括垃圾收集器在实现层面上实际执行的操作,导致请求时不能进行垃圾回收。这个特征只是一个nop吗?是否要满足某些条件?这些条件是什么?

就目前而言,Java的GC经常看起来像是一个你不能信任的怪物。你不知道它何时出现或消失,不知道它会做什么,以及它如何做到这一点。我可以想象一些专家对每个指令如何工作有更好的了解,但绝大多数人只是希望它“正常工作”,不得不依赖一个看起来不透明的算法来为你做事是令人沮丧的。

阅读或学习某些东西与实际看到其实现之间存在很大的差距、在不同系统之间的差异以及能够玩弄它而不必查看源代码。这会产生信心和掌握/理解/控制感。

总之,答案中存在固有的问题:“这个功能可能什么也不做,我不会详细说明何时会发生什么以及为什么会发生,通常意味着试图这样做是不合理的哲学。”

Java垃圾回收机制可能表现得还可以,也可能不行,但要理解它,真正跟进它运作的方向,以获得全面的概述并信任GC能够做什么和不能做什么是困难的。所以很容易就不信任这门语言,因为语言的目的是在哲学层面上拥有可控行为(尤其是对于程序员,特别是新手,从某些系统/语言行为中会陷入存在危机),只有你具备能够容忍的程度(如果你无法容忍,就不会使用该语言,直到必须使用),还有更多的事情,因为没有明显的原因而无法控制,这本质上是有害的。


11
有时候(但并不经常!)您确实比运行时间更了解过去、当前和未来的内存使用情况。在正常页面被服务时,这种情况并不经常发生,甚至可以说永远不会发生在 Web 应用程序中。
许多年前,我曾经参与过一个报告生成器的开发:
  • 它只有一个线程
  • 从队列中读取“报告请求”
  • 从数据库中加载报告所需的数据
  • 生成报告并通过电子邮件发送
  • 一直重复执行,当没有未完成的请求时进行睡眠
  • 它不会重复使用任何报告之间的数据,并且也不会进行任何缓存
首先,由于它并不是实时的,用户期望等待报告,因此 GC 运行时的延迟并不是问题。但是,我们需要以比请求速度更快的速度生成报告。
通过观察上述流程可知:
  • 在发送报告后,很少有活动对象,因为下一个请求尚未开始处理。
  • 众所周知,垃圾回收周期的成本取决于活动对象的数量,而垃圾量对 GC 运行的成本影响很小。
  • 当队列为空时,运行 GC 是最好的选择。
因此,每当请求队列为空时进行一次 GC 运行是非常值得的;这样做没有任何副作用。
在发送每份报告后进行一次 GC 运行可能值得考虑,因为我们知道这是一个好时机。但是,如果计算机有足够的内存,则延迟 GC 运行会获得更好的结果。
这种行为是根据每个安装配置的,对于某些客户,启用每份报告后强制运行 GC 程序极大地加快了报告的生产速度。(我认为这是由于他们服务器上的低内存以及运行许多其他进程,因此良好时机的强制 GC 可以减少分页。)

我们从未发现过任何一次安装不会从在工作队列为空时强制运行GC中受益。

但是,请明确,上述情况并不常见。

现在,我更倾向于在单独的进程中运行每个报告,让操作系统清理内存而不是垃圾收集器,并且在大型服务器上使用自定义队列管理服务使用多个工作进程。


9

GC效率依赖于许多启发式算法。例如,一种常见的启发式算法是写入对象通常发生在不久前创建的对象上。另一个是许多对象的寿命非常短暂(有些对象将被长时间使用,但许多对象将在其创建后的几微秒内被丢弃)。

调用System.gc()就像是踢动垃圾回收。这意味着:“所有那些经过精细调整的参数,那些聪明的组织,所有你刚刚为了分配和管理对象而付出的努力,都要全部抛弃,从头开始。”它可能会提高性能,但大多数时候只会降低性能。

要可靠地使用System.gc(),您需要了解垃圾回收器在所有细节方面的操作。如果使用另一个供应商的JVM,或者来自同一供应商的下一个版本,或者具有略微不同的命令行选项的相同JVM,则此类详细信息往往会发生相当大的变化。因此,除非您想解决控制所有这些参数的特定问题,否则这很少是一个好主意。因此有“不良实践”的概念:这不是被禁止的,该方法存在,但它很少有回报。

(*)我在这里谈论的是效率。 System.gc()不会破坏正确的Java程序。即使作为最后手段,在抛出OutOfMemoryError之前,JVM也会执行System.gc()的工作,而不是凭空创造额外的内存。


1
+1 表示 System.gc() 不会防止 OutOfMemoryError。有些人认为它可以。 - sleske
1
实际上,它可能会防止OutOfMemoryError的发生,因为软引用的处理方式。在我所知道的实现中,最后一次GC运行后创建的SoftReferences不会被收集。但这是一个随时可能改变的实现细节和某种错误,你不应该依赖它。 - maaartinus

4
也许我的代码写得不太好,但我意识到在Eclipse和NetBeans IDE中点击垃圾桶图标是一种“好的实践”。

1
这可能是这样。但是,如果Eclipse或NetBeans被编程定期调用System.gc(),您可能会发现这种行为很烦人。 - Stephen C

3

我即将写的一些内容只是对其他答案已经写过的内容进行概括,同时还有一些新的内容。

问题“为什么调用System.gc()是一种不好的做法?”并不成立。它假设这是一种不好的做法,但实际上并不是。它非常依赖于你想要实现什么目标。

绝大多数程序员没有必要使用System.gc(),在绝大多数情况下它也不会对他们产生任何有用的作用。因此,对于大多数人来说,调用它是一种不好的做法,因为它不会做任何他们认为它会做的事情,只会增加开销。

然而,在极少数情况下,调用System.gc()确实是有益的:

  1. 当你确定现在有一些CPU时间可以空闲,并且想要提高稍后运行的代码的吞吐量时,可以使用System.gc()。例如,Web服务器发现当前没有挂起的Web请求时,可以立即启动垃圾回收,以降低稍后处理大量Web请求期间需要进行垃圾回收的可能性。(当然,如果在回收期间出现Web请求,这可能会造成影响,但是Web服务器可以聪明地放弃回收操作,以便当请求到来时能够及时响应。)桌面GUI也是一个例子:在空闲事件(或更广泛地说,在一段时间的不活动之后),您可以提示JVM,如果它有任何垃圾回收工作要做,现在比以后更好。
  2. 当你想要检测内存泄漏时,可以使用System.gc()。这通常与调试模式下的终结器或Java 9中引入的java.lang.ref.Cleaner类结合使用。其思想是通过立即强制进行垃圾回收,因此在未来的某个随机时间而不是现在发现内存泄漏,您可以尽快检测到内存泄漏,从而更好地确定哪段代码泄漏了内存以及为什么泄漏。(顺便说一句,这也是终结器或Cleaner的唯一合法用例之一。尽管非常普遍甚至被官方推荐,但使用终结器回收未管理资源的做法存在缺陷,因为它是不确定的。有关此主题的更多信息,请阅读此文:https://blog.michael.gr/2021/01/object-lifetime-awareness.html
  3. 当你在测量代码性能(基准测试)时,可以使用System.gc()来减少/最小化基准测试期间发生垃圾回收的可能性,或者保证由于基准测试期间产生的垃圾而遭受的任何开销都是由基准测试代码生成的,而不是由无关代码生成的。一个好的基准测试总是从尽可能彻底的垃圾回收开始。
  4. 当你在测量代码内存消耗时,可以使用System.gc()来确定一段代码生成了多少垃圾。其思想是执行完整的垃圾回收以启动干净状态,运行要测量的代码,获取堆大小,然后再执行一次完整的垃圾回收,再次获取堆大小,并取两者之差。(顺便说一句,在运行要测量的代码时能够暂时禁止垃圾回收会很有用,但JVM不支持此功能。这是令人遗憾的。)

请注意,上述用例中,只有一个在生产场景中使用;其余都是在测试/诊断场景中使用。

这意味着System.gc()在某些情况下可能非常有用,这反过来又意味着它“只是提示”的问题。

只要JVM没有提供一种确定性和可靠的垃圾回收控制方式,JVM在这方面就有缺陷。
以下是如何将 System.gc() 转换成更少提示的方法:
private static void runGarbageCollection()
{
    for( WeakReference<Object> ref = new WeakReference<>( new Object() ); ; )
    {
        System.gc(); //optional
        Runtime.getRuntime().runFinalization(); //optional
        if( ref.get() == null )
            break;
        Thread.yield();
    }
}

这仍然不能保证您会获得完整的垃圾回收,但它会更接近。具体而言,即使使用了-XX:DisableExplicitGC VM选项,它也会为您提供一定量的垃圾回收。(因此,它确实将System.gc()用作提示;它不依赖于它。)


1

是的,调用System.gc()并不能保证它会运行,它只是向JVM发出的一个请求,可能会被忽略。从文档中可以看到:

调用gc方法建议Java虚拟机花费精力回收未使用的对象

通常情况下,调用它几乎总是不明智的,因为自动内存管理通常比你更清楚何时进行垃圾回收。当其内部的空闲内存池较低或操作系统请求返回一些内存时,它将进行垃圾回收。

如果你知道它有帮助,那么调用System.gc()可能是可以接受的。我的意思是,在部署平台上彻底测试和测量了两种情况的行为,并且可以证明它有帮助。但要注意,垃圾回收并不容易预测 - 它可能在一次运行中有所帮助,在另一次运行中却有所损失。


但是从Javadoc文档中也可以看到:当方法调用结束时,虚拟机会尽其所能地回收所有被丢弃的对象,我认为这是您发布内容更为明确的形式。哎呀,别管它,已经有一个关于这个描述误导的错误报告了。既然我们不知道其中的坑,提示JVM可能会带来哪些问题? - zneak
1
危害在于在错误的时间进行垃圾回收可能会导致巨大的减速。你提供的提示可能是错误的。至于“尽力而为”的评论,请在像JConsole这样的工具中尝试并查看。有时单击“执行GC”按钮不起作用。 - tom
很抱歉不同意,但在OpenJDK及其基于它的任何东西(例如HP)中调用System.gc()总是会导致垃圾回收周期。事实上,IBM的J9实现似乎也是如此。 - Kirk
1
@Kirk - 不正确:谷歌并阅读关于-XX:-DisableExplicitGC的内容。 - Stephen C

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