Java 为什么调用 System.gc() 是不好的做法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2414105/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why is it bad practice to call System.gc()?
提问by zneak
After answeringa question about how to force-free objects in Java(the guy was clearing a 1.5GB HashMap) with System.gc()
, I was told it's bad practice to call System.gc()
manually, but the comments were not entirely convincing. In addition, no one seemed to dare to upvote, nor downvote my answer.
在回答了一个关于如何在 Java 中强制释放对象(那个人正在清除 1.5GB HashMap)的问题后System.gc()
,我被告知System.gc()
手动调用是一种不好的做法,但这些评论并不完全令人信服。此外,似乎没有人敢于赞成或反对我的回答。
I was told there that it's bad practice, but then I was also told that garbage collector runs don't systematically stop the world anymore, and that it could also effectively be used by the JVM only as a hint, so I'm kind of at loss.
有人告诉我这是不好的做法,但后来我也被告知垃圾收集器运行不再系统地停止世界,并且它也可以被 JVM 有效地用作提示,所以我有点亏本。
I do understand that the JVM usually knows better than you when it needs to reclaim memory. I also understand that worrying about a few kilobytes of data is silly. I also understand that even megabytes of data isn't what it was a few years back. But still, 1.5 gigabytes? And you knowthere's like 1.5 GB of data hanging around in memory; it's not like it's a shot in the dark. Is System.gc()
systematically bad, or is there some point at which it becomes okay?
我确实理解 JVM 在需要回收内存时通常比您更了解。我也明白担心几千字节的数据是愚蠢的。我也明白,即使是兆字节的数据也不是几年前的样子。但仍然是 1.5 GB?而且您知道内存中大约有 1.5 GB 的数据;这不像是在黑暗中拍摄。是System.gc()
系统性的坏事,还是在某个点上变得好起来?
So the question is actually double:
所以问题实际上是双重的:
- 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 nevera good idea to call
System.gc()
, or are there times when it's acceptable? If so, what are those times?
- 为什么打电话不是坏习惯
System.gc()
?它真的只是在某些实现下对 JVM 的一个提示,还是总是一个完整的收集周期?真的有垃圾收集器实现可以在不停止世界的情况下完成它们的工作吗?请阐明人们在对我的回答的评论中所做的各种断言。 - 门槛在哪里?它是从来没有一个好主意,通话
System.gc()
,还是有时间当它是可以接受的?如果有,那是什么时间?
采纳答案by Steven Schlansker
The reason everyone always says to avoid System.gc()
is that it is a pretty good indicator of fundamentally broken code. Any code that depends on it for correctness is certainly broken; any that rely on it for performance are most likely broken.
每个人都说要避免的原因System.gc()
是,它是代码根本性损坏的一个很好的指标。任何依赖于它的正确性的代码肯定都被破坏了;任何依赖于它的性能最有可能被破坏。
You don't know what sort of garbage collector you are running under. There are certainly some that do not "stop the world"as you assert, but some JVMs aren't that smart or for various reasons (perhaps they are on a phone?) don't do it. You don't know what it's going to do.
你不知道你正在运行什么样的垃圾收集器。当然,有些 JVM 不会像您断言的那样“停止世界”,但有些 JVM 并不那么聪明,或者出于各种原因(也许它们在手机上?)不这样做。你不知道它会做什么。
Also, it's not guaranteed to do anything. The JVM may just entirely ignore your request.
此外,它不能保证做任何事情。JVM 可能会完全忽略您的请求。
The combination of "you don't know what it will do," "you don't know if it will even help," and "you shouldn't need to call it anyway" are why people are so forceful in saying that generally you shouldn't call it. I think it's a case of "if you need to ask whether you should be using this, you shouldn't"
“你不知道它会做什么”、“你甚至不知道它是否会有所帮助”和“无论如何你都不应该打电话给它”的组合就是为什么人们通常如此强硬地说你不应该叫它。我认为这是“如果你需要问你是否应该使用它,你不应该”的情况
EDITto address a few concerns from the other thread:
编辑以解决其他线程中的一些问题:
After reading the thread you linked, there's a few more things I'd like to point out.
First, someone suggested that calling gc()
may return memory to the system. That's certainly not necessarily true - the Java heap itself grows independently of Java allocations.
阅读您链接的线程后,我还想指出几件事。首先,有人建议调用gc()
可能会将内存返回给系统。这当然不一定是真的 - Java 堆本身的增长独立于 Java 分配。
As in, the JVM will hold memory (many tens of megabytes) and grow the heap as necessary. It doesn't necessarily return that memory to the system even when you free Java objects; it is perfectly free to hold on to the allocated memory to use for future Java allocations.
如在,JVM 将持有内存(数十兆字节)并根据需要增加堆。即使您释放 Java 对象,它也不一定会将内存返回给系统;保留分配的内存以用于将来的 Java 分配是完全自由的。
To show that it's possible that System.gc()
does nothing, view:
要表明可能System.gc()
什么都不做,请查看:
http://bugs.sun.com/view_bug.do?bug_id=6668279
http://bugs.sun.com/view_bug.do?bug_id=6668279
and in particular that there's a -XX:DisableExplicitGCVM option.
特别是有一个-XX:DisableExplicitGCVM 选项。
回答by tom
Yes, calling System.gc() doesn't guarantee that it will run, it's a request to the JVM that may be ignored. From the docs:
是的,调用 System.gc() 并不能保证它会运行,它是对 JVM 的请求,可能会被忽略。从文档:
Calling the gc method suggests that the Java Virtual Machine expend effort toward recycling unused objects
调用 gc 方法表明 Java 虚拟机花费精力来回收未使用的对象
It's almost always a bad idea to call it because the automatic memory management usually knows better than you when to gc. It will do so when its internal pool of free memory is low, or if the OS requests some memory be handed back.
调用它几乎总是一个坏主意,因为自动内存管理通常比您更了解何时进行 gc。当它的内部可用内存池低时,或者如果操作系统请求交还一些内存,它就会这样做。
It might be acceptable to call System.gc() if you knowthat it helps. By that I mean you've thoroughly tested and measured the behaviour of both scenarios on the deployment platform, and you can show it helps. Be aware though that the gc isn't easily predictable - it may help on one run and hurt on another.
如果您知道它有帮助,那么调用 System.gc() 可能是可以接受的。我的意思是您已经在部署平台上彻底测试和测量了两种场景的行为,并且可以证明它有帮助。请注意,尽管 gc 不容易预测 - 它可能在一次运行中有所帮助,而在另一次运行中可能会受到伤害。
回答by Stephen C
It has already been explained that calling system.gc()
maydo nothing, and that any code that "needs" the garbage collector to run is broken.
已经解释过调用system.gc()
可能什么都不做,任何“需要”垃圾收集器运行的代码都被破坏了。
However, the pragmatic reason that it is bad practice to call System.gc()
is that it is inefficient. And in the worst case, it is horribly inefficient! Let me explain.
然而,调用System.gc()
是不好的实践的实际原因是它效率低下。在最坏的情况下,它的效率非常低!让我解释。
A typical GC algorithm identifies garbage by traversing all non-garbage objects in the heap, and inferring that any object not visited must be garbage. From this, we can model the total work of a garbage collection consists of one part that is proportional to the amount of live data, and another part that is proportional to the amount of garbage; i.e. work = (live * W1 + garbage * W2)
.
典型的 GC 算法通过遍历堆中所有非垃圾对象来识别垃圾,并推断任何未被访问的对象一定是垃圾。由此,我们可以对垃圾收集的总工作进行建模,其中一部分与实时数据量成正比,另一部分与垃圾量成正比;即work = (live * W1 + garbage * W2)
。
Now suppose that you do the following in a single-threaded application.
现在假设您在单线程应用程序中执行以下操作。
System.gc(); System.gc();
The first call will (we predict) do (live * W1 + garbage * W2)
work, and get rid of the outstanding garbage.
第一个调用(我们预测)会(live * W1 + garbage * W2)
起作用,并清除未完成的垃圾。
The second call will do (live* W1 + 0 * W2)
work and reclaim nothing. In other words we have done (live * W1)
work and achieved absolutely nothing.
第二个调用将(live* W1 + 0 * W2)
起作用并且不回收任何东西。换句话说,我们做了(live * W1)
工作,却一无所获。
We can model the efficiency of the collector as the amount of work needed to collect a unit of garbage; i.e. efficiency = (live * W1 + garbage * W2) / garbage
. So to make the GC as efficient as possible, we need to maximizethe value of garbage
when we run the GC; i.e. wait until the heap is full. (And also, make the heap as big as possible. But that is a separate topic.)
我们可以将收集器的效率建模为收集单位垃圾所需的工作量;即efficiency = (live * W1 + garbage * W2) / garbage
。所以为了让GC尽可能高效,我们需要最大化garbage
运行GC时的值;即等到堆满。(而且,使堆尽可能大。但这是一个单独的主题。)
If the application does not interfere (by calling System.gc()
), the GC will wait until the heap is full before running, resulting in efficient collection of garbage1. But if the application forces the GC to run, the chances are that the heap won't be full, and the result will be that garbage is collected inefficiently. And the more often the application forces GC, the more inefficient the GC becomes.
如果应用程序不干扰(通过调用System.gc()
),GC 将等到堆满后再运行,从而有效地收集垃圾1。但如果应用程序强制 GC 运行,则堆可能不会满,结果将是垃圾收集效率低下。应用程序强制 GC 的频率越高,GC 的效率就越低。
Note: the above explanation glosses over the fact that a typical modern GC partitions the heap into "spaces", the GC may dynamically expand the heap, the application's working set of non-garbage objects may vary and so on. Even so, the same basic principal applies across the board to all true garbage collectors2. It is inefficient to force the GC to run.
注意:上面的解释掩盖了这样一个事实,即典型的现代 GC 将堆划分为“空间”,GC 可能会动态扩展堆,应用程序的非垃圾对象工作集可能会发生变化等等。即便如此,相同的基本原则也适用于所有真正的垃圾收集器2。强制 GC 运行效率低下。
1 - This is how the "throughput" collector works. Concurrent collectors such as CMS and G1 use different criteria to decide when to start the garbage collector.
1 - 这就是“吞吐量”收集器的工作方式。CMS 和 G1 等并发收集器使用不同的标准来决定何时启动垃圾收集器。
2 - I'm also excluding memory managers that use reference counting exclusively, but no current Java implementation uses that approach ... for good reason.
2 - 我也排除了专门使用引用计数的内存管理器,但当前没有 Java 实现使用这种方法......这是有充分理由的。
回答by JT.
People have been doing a good job explaining why NOT to use, so I will tell you a couple situations where you should use it:
人们一直在很好地解释为什么不使用它,所以我会告诉你应该使用它的几种情况:
(The following comments apply to Hotspot running on Linux with the CMS collector, where I feel confident saying that System.gc()
does in fact always invoke a full garbage collection).
(以下评论适用于使用 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.
在启动您的应用程序的初始工作之后,您的内存使用状态可能会很糟糕。你的老一代可能有一半充满了垃圾,这意味着你离你的第一个 CMS 更近了。在重要的应用程序中,调用 System.gc() 将堆“重置”到实时数据的起始状态并不是一个坏主意。
与#1 相同,如果您密切监视堆使用情况,您希望准确读取基准内存使用情况。如果您的应用程序正常运行时间的前 2 分钟都是初始化,那么除非您预先强制(咳咳……“建议”)完整的 gc,否则您的数据将被弄乱。
您可能有一个应用程序,它被设计为在运行时从不向年老代提升任何东西。但也许你需要预先初始化一些数据,这些数据并不大到自动移动到年老代。除非你在一切都设置好之后调用 System.gc(),否则你的数据可能会留在新一代中,直到它被提升的时候。突然之间,您的超级低延迟、低 GC 应用程序因在正常操作期间提升这些对象而遭受巨大(当然,相对而言)延迟损失。
在生产应用程序中使用 System.gc 调用来验证内存泄漏的存在有时很有用。如果您知道时间 X 的一组实时数据应该与时间 Y 的一组实时数据以一定比例存在,那么调用 System.gc() 时间 X 和时间 Y 并比较内存使用情况可能会很有用.
回答by Ryan Fernandes
Maybe I write crappy code, but I've come to realize that clicking the trash-can icon on eclipse and netbeans IDEs is a 'good practice'.
也许我编写了蹩脚的代码,但我开始意识到单击 eclipse 和 netbeans IDE 上的垃圾桶图标是一个“好习惯”。
回答by Thomas Pornin
GC efficiency relies on a number of heuristics. For instance, a common heuristic is that write accesses to objects usually occur on objects which were created not long ago. Another is that many objects are very short-lived (some objects will be used for a long time, but many will be discarded a few microseconds after their creation).
GC 效率依赖于许多启发式方法。例如,一个常见的启发式方法是对对象的写访问通常发生在不久前创建的对象上。另一个是许多对象的生命周期很短(有些对象会使用很长时间,但许多对象会在创建后几微秒内被丢弃)。
Calling System.gc()
is like kicking the GC. It means: "all those carefully tuned parameters, those smart organizations, all the effort you just put into allocating and managing the objects such that things go smoothly, well, just drop the whole lot, and start from scratch". It mayimprove performance, but most of the time it just degradesperformance.
调用System.gc()
就像踢 GC。它的意思是:“所有那些精心调整的参数,那些聪明的组织,你在分配和管理对象上所做的所有努力,以便事情顺利进行,好吧,放下所有的一切,从头开始”。它可能会提高性能,但大多数时候它只会降低性能。
To use System.gc()
reliably(*) you need to know how the GC operates in all its fine details. Such details tend to change quite a bit if you use a JVM from another vendor, or the next version from the same vendor, or the same JVM but with slightly different command-line options. So it is rarely a good idea, unless you want to address a specific issue in which you control all those parameters. Hence the notion of "bad practice": that's not forbidden, the method exists, but it rarely pays off.
要System.gc()
可靠地使用(*),您需要了解 GC 的所有细节是如何运作的。如果您使用来自其他供应商的 JVM,或者来自同一供应商的下一个版本,或者相同的 JVM,但命令行选项略有不同,这些细节往往会发生很大变化。因此,这很少是一个好主意,除非您想解决控制所有这些参数的特定问题。因此,“不良做法”的概念是:这不是被禁止的,该方法存在,但很少有回报。
(*) I am talking about efficiency here. System.gc()
will never breaka correct Java program. It will neither conjure extra memory that the JVM could not have obtained otherwise: before throwing an OutOfMemoryError
, the JVM does the job of System.gc()
, even if as a last resort.
(*) 我在这里谈论的是效率。System.gc()
永远不会破坏正确的 Java 程序。它既不会产生 JVM 无法获得的额外内存:在抛出 之前OutOfMemoryError
,JVM 会完成 的工作System.gc()
,即使是最后的手段。
回答by KitsuneYMG
Lots of people seem to be telling you not to do this. I disagree. If, after a large loading process like loading a level, you believe that:
很多人似乎告诉你不要这样做。我不同意。如果在像加载关卡这样的大型加载过程之后,您认为:
- You have a lot of objects that are unreachable and may not have been gc'ed. and
- You think the user could put up with a small slowdown at this point
- 你有很多无法访问的对象,可能没有被 gc'ed。和
- 您认为此时用户可以忍受一点点减速
there is no harm in calling System.gc(). I look at it like the c/c++ inline
keyword. It's just a hint to the gc that you, the developer, have decided that time/performance is not as important as it usually is and that some of it could be used reclaiming memory.
调用 System.gc() 没有坏处。我把它看成 c/c++inline
关键字。这只是对 gc 的一个提示,您作为开发人员,已经决定时间/性能不像通常那样重要,并且其中一些可以用于回收内存。
Advice to not rely on it doing anything is correct. Don't rely on it working, but giving the hint that now is an acceptable time to collect is perfectly fine. I'd rather waste time at a point in the code where it doesn't matter (loading screen) than when the user is actively interacting with the program (like during a level of a game.)
建议不要依赖它做任何事情是正确的。不要依赖它的工作,但提示现在是一个可以接受的收集时间是完全没问题的。我宁愿在代码中无关紧要的点(加载屏幕)浪费时间,也不愿在用户主动与程序交互时(例如在游戏关卡中)浪费时间。
There is one time when i will forcecollection: when attempting to find out is a particular object leaks (either native code or large, complex callback interaction. Oh and any UI component that so much as glances at Matlab.) This should never be used in production code.
有一次我会强制收集:当试图找出特定对象泄漏时(本机代码或大型复杂的回调交互。哦,以及任何看一眼 Matlab 的 UI 组件。)这不应该被使用在生产代码中。
回答by sleske
In my experience, using System.gc() is effectively a platform-specific form of optimization (where "platform" is the combination of hardware architecture, OS, JVM version and possible more runtime parameters such as RAM available), because its behaviour, while roughly predictable on a specific platform, can (and will) vary considerably between platforms.
根据我的经验,使用 System.gc() 实际上是一种特定于平台的优化形式(其中“平台”是硬件架构、操作系统、JVM 版本和可能的更多运行时参数(如可用 RAM)的组合),因为它的行为,虽然在特定平台上可以大致预测,但在不同平台之间可以(并且将会)有很大差异。
Yes, there aresituations where System.gc() will improve (perceived) performance. On example is if delays are tolerable in some parts of your app, but not in others (the game example cited above, where you want GC to happen at the start of a level, not during the level).
是的,在某些情况下 System.gc() 会提高(感知)性能。例如,您的应用程序的某些部分是否可以容忍延迟,但在其他部分则不能容忍(上面引用的游戏示例,您希望 GC 在关卡开始时发生,而不是在关卡期间)。
However, whether it will help or hurt (or do nothing) is highlydependent on the platform (as defined above).
然而,它是否会帮助或伤害(或什么都不做)高度依赖于平台(如上定义)。
So I think it is valid as a last-resort platform-specific optimization (i.e. if other performance optimizations are not enough). But you should never call it just because you believe it might help(without specific benchmarks), because chances are it will not.
所以我认为它作为最后的平台特定优化是有效的(即如果其他性能优化还不够)。但是你永远不应该仅仅因为你相信它可能有帮助(没有特定的基准)就调用它,因为它很可能不会。
回答by Kirk
First, there is a difference between spec and reality. The spec says that System.gc() is a hint that GC should run and the VM is free to ignore it. The reality is, the VM will neverignore a call to System.gc().
首先,规范和现实之间存在差异。规范说 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.
调用 GC 会给调用带来不小的开销,如果您在某个随机时间点执行此操作,您的努力可能不会得到任何回报。另一方面,自然触发的收款很可能会收回通话费用。如果您有信息表明应该运行 GC,那么您可以调用 System.gc() 并且您应该会看到好处。但是,根据我的经验,这只会发生在少数极端情况下,因为您不太可能有足够的信息来了解是否以及何时应该调用 System.gc()。
One example listed here, hitting the garbage can in your IDE. If you're off to a meeting why not hit it. The overhead isn't going to affect you and heap might be cleaned up for when you get back. Do this in a production system and frequent calls to collect will bring it to a grinding halt! Even occasional calls such as those made by RMI can be disruptive to performance.
这里列出了一个例子,在你的 IDE 中点击垃圾桶。如果你要去参加一个会议,为什么不去参加呢。开销不会影响你,当你回来时,堆可能会被清理干净。在生产系统中执行此操作,频繁调用 collect 将使其陷入停顿!即使是偶尔的调用(例如 RMI 发出的调用)也会对性能造成破坏。
回答by Ian Ringrose
Sometimes (not often!) you do truly know more about past, current and future memory usage then the run time does. This does not happen very often, and I would claim never in a web application while normal pages are being served.
有时(不经常!)你确实比运行时更了解过去、当前和未来的内存使用情况。这并不经常发生,我会声称在 Web 应用程序中永远不会在提供正常页面的情况下发生。
Many year ago I work on a report generator, that
许多年前,我在研究报告生成器,即
- Had a single thread
- Read the “report request” from a queue
- Loaded the data needed for the report from the database
- Generated the report and emailed it out.
- Repeated forever, sleeping when there were no outstanding requests.
- It did not reuse any data between reports and did not do any cashing.
- 有一个线程
- 从队列中读取“报告请求”
- 从数据库加载报表所需的数据
- 生成报告并通过电子邮件发送出去。
- 永远重复,在没有未完成的请求时睡觉。
- 它没有在报告之间重复使用任何数据,也没有进行任何兑现。
Firstly as it was not real time and the users expected to wait for a report, a delay while the GC run was not an issue, but we needed to produce reports at a rate that was faster than they were requested.
首先,因为它不是实时的并且用户希望等待报告,所以 GC 运行时的延迟不是问题,但我们需要以比他们要求的速度更快的速度生成报告。
Looking at the above outline of the process, it is clear that.
看上面的流程大纲,很明显。
- We know there would be very few live objects just after a report had been emailed out, as the next request had not started being processed yet.
- It is well known that the cost of running a garbage collection cycle is depending on the number of live objects,the amount of garbage has little effect on the cost of a GC run.
- That when the queue is empty there is nothing better to do then run the GC.
- 我们知道,在通过电子邮件发送报告后,活动对象很少,因为下一个请求尚未开始处理。
- 众所周知,运行一个垃圾回收周期的成本取决于存活对象的数量,垃圾数量对一次 GC 运行的成本影响不大。
- 当队列为空时,没有什么比运行 GC 更好的了。
Therefore clearly it was well worth while doing a GC run whenever the request queue was empty; there was no downside to this.
因此,很明显,每当请求队列为空时执行 GC 运行是非常值得的;这没有任何不利之处。
It may be worth doing a GC run after each report is emailed, as we know this is a good time for a GC run. However if the computer had enough ram, better results would be obtained by delaying the GC run.
在通过电子邮件发送每个报告后,可能值得进行一次 GC 运行,因为我们知道这是运行 GC 的好时机。但是,如果计算机有足够的内存,则通过延迟 GC 运行可以获得更好的结果。
This behaviour was configured on a per installation bases, for somecustomers enabling a forced GC after each report greatly speededup the protection of reports. (I expect this was due to low memory on their server and it running lots of other processes, so hence a well time forced GC reduced paging.)
此行为是在每个安装基础上配置的,对于某些客户而言,在每次报告后启用强制 GC极大地加快了报告的保护速度。(我预计这是由于他们的服务器内存不足,并且它运行了许多其他进程,因此强制 GC 减少了分页。)
We never detected an installation that did not benefit was a forced GC run every time the work queue was empty.
我们从未检测到每次工作队列为空时强制 GC 运行没有好处的安装。