我将要写的一些内容只是对其他答案中已经写过的内容的总结,还有一些是新的。
“为什么调用System.gc()是不好的做法?”这个问题没有计算出来。它假定这是不好的做法,但事实并非如此。这在很大程度上取决于你想要完成什么。
绝大多数程序员不需要System.gc(),而且在绝大多数用例中,它永远不会对他们做任何有用的事情。因此,对于大多数人来说,调用它是一种糟糕的做法,因为它不会做他们认为它会做的任何事情,它只会增加开销。
然而,在极少数情况下,调用System.gc()实际上是有益的:
When you are absolutely sure that you have some CPU time to spare now, and you want to improve the throughput of code that will run later. For example, a web server that discovers that there are no pending web requests at the moment can initiate garbage collection now, so as to reduce the chances that garbage collection will be needed during the processing of a barrage of web requests later on. (Of course this can hurt if a web request arrives during collection, but the web server could be smart about it and abandon collection if a request comes in.) Desktop GUIs are another example: on the idle event (or, more broadly, after a period of inactivity,) you can give the JVM a hint that if it has any garbage collection to do, now is better than later.
When you want to detect memory leaks. This is often done in combination with a debug-mode-only finalizer, or with the java.lang.ref.Cleaner class from Java 9 onwards. The idea is that by forcing garbage collection now, and thus discovering memory leaks now as opposed to some random point in time in the future, you can detect the memory leaks as soon as possible after they have happened, and therefore be in a better position to tell precisely which piece of code has leaked memory and why. (Incidentally, this is also one of, or perhaps the only, legitimate use cases for finalizers or the Cleaner. The practice of using finalization for recycling of unmanaged resources is flawed, despite being very widespread and even officially recommended, because it is non-deterministic. For more on this topic, read this: https://blog.michael.gr/2021/01/object-lifetime-awareness.html)
When you are measuring the performance of code, (benchmarking,) in order to reduce/minimize the chances of garbage collection occurring during the benchmark, or in order to guarantee that whatever overhead is suffered due to garbage collection during the benchmark is due to garbage generated by the code under benchmark, and not by unrelated code. A good benchmark always starts with an as thorough as possible garbage collection.
When you are measuring the memory consumption of code, in order to determine how much garbage is generated by a piece of code. The idea is to perform a full garbage collection so as to start in a clean state, run the code under measurement, obtain the heap size, then do another full garbage collection, obtain the heap size again, and take the difference. (Incidentally, the ability to temporarily suppress garbage collection while running the code under measurement would be useful here, alas, the JVM does not support that. This is deplorable.)
请注意,在上面的用例中,只有一个是在生产场景中;其余的在测试/诊断场景中。
这意味着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();
}
}
这仍然不能保证您将得到一个完整的GC,但它已经很接近了。具体来说,即使使用了-XX:DisableExplicitGC VM选项,它也会为您提供一定数量的垃圾收集。(因此,它真正使用System.gc()作为提示;它并不依赖于它。)