System.gc() 什么时候会发挥作用?

131
我知道Java中的垃圾回收是自动的。但我了解到,如果在代码中调用System.gc(),JVM可能会决定在那个时刻进行或不进行垃圾回收。这是如何精确工作的?JVM基于什么准则/参数来决定是否执行GC,当它看到System.gc()时?
是否有任何例子表明将此放入代码中是一个好主意?

4
这个概念过于复杂,无法在此进行详细说明,但这篇文章应该可以帮到你:http://chaoticjava.com/posts/how-does-garbage-collection-work/ - GEOCHET
具体行为取决于JVM,即实现相关。 - Thorbjørn Ravn Andersen
16个回答

62

实际上,它通常会决定执行垃圾回收。答案因许多因素而异,如您正在运行的JVM、所处模式以及使用的垃圾回收算法。

我不建议在代码中依赖它。如果JVM即将抛出OutOfMemoryError,调用System.gc()不会阻止它,因为垃圾收集器会尽可能释放更多内存,直到极限。我见过它被用于IDE中,那里附有一个用户可以点击的按钮,但即使在那里,它也不是非常有用。


4
你可以在jconsole中强制进行垃圾回收。 - Benedikt Waldvogel
25
你能真正"强制"它吗?jconsole只是在调用System.gc()吗? - John Meagher
4
在视频游戏的加载界面中,我会使用System.gc()方法。此时,我并不介意这个调用是否清除了所有可能的东西,甚至什么也没做。然而,我更希望它在加载界面期间“努力回收未使用的对象”,而不是在游戏进行时进行回收。 - Kröw

30

在Java中,垃圾回收是由虚拟机决定的,您无法对其进行控制。我从未遇到过需要使用System.gc()的情况。由于System.gc()调用只是暗示虚拟机执行垃圾回收,并且它会执行一个完整的垃圾回收(在多代堆中包括老年代和新生代),因此它实际上可能会消耗比必要更多的CPU周期。

在某些情况下,建议虚拟机立即进行一次完整的垃圾回收可能是有意义的,例如您知道应用程序在接下来的几分钟内将处于空闲状态,然后会产生大量工作负载。例如,在应用程序启动期间初始化了大量临时对象之后(即,我刚刚缓存了大量信息,并且我知道在接下来的一分钟或更长时间内不会有太多活动)。想象一下像Eclipse这样的IDE启动 - 它需要进行很多初始化操作,因此在初始化之后立即执行完整的垃圾回收可能是有意义的。


30

我唯一能想到需要调用System.gc()方法的情况是,在对一个应用程序进行性能分析以寻找可能的内存泄漏时。我相信性能分析器在捕获内存快照之前会调用这个方法。


4
是的,我也是这样做的。例如,Runtime.freeMemory() 返回的值只有在执行System.gc() 后才真正有意义,因为通常您想知道有多少内存被不可回收的实例阻塞了。当然,这仅用于调试/内存分析,永远不要在生产中使用。 - sleske
不,它也适用于许多其他情况。例如,假设您有一个生命周期感知的单个模式。在onStop()中,如果您将实例设置为null,则调用System.gc()有助于它。 - portfoliobuilder

27

Java语言规范并不保证在调用System.gc()时JVM会启动GC。这就是为什么出现了“可能会决定在那个点上执行GC,也可能不会”的原因。

现在,如果您查看作为Oracle JVM骨干的OpenJDK源代码,您会发现调用System.gc()确实会启动一个GC周期。如果您使用另一个JVM,例如J9,则必须查阅其文档以找到答案。例如,Azul的JVM具有连续运行的垃圾收集器,因此调用System.gc()将不起作用。

一些其他答案提到通过JConsole或VisualVM启动GC。基本上,这些工具会远程调用System.gc()

通常,您不希望从代码中启动垃圾回收循环,因为它会破坏应用程序的语义。您的应用程序执行某些业务操作,JVM负责内存管理。您应该将这些问题分开处理(不要让应用程序执行一些内存管理操作,而是专注于业务)。

然而,在一些情况下,调用System.gc()可能是可以理解的。例如,微基准测试。没有人希望在微基准测试中发生GC周期。因此,您可以在每次测量之间触发一次GC循环,以确保每个测量都以空堆开始。


17

如果您调用System.gc(),需要非常小心。调用它可能会给您的应用程序增加不必要的性能问题,并且不能保证实际执行垃圾回收。实际上,可以通过Java参数-XX:+DisableExplicitGC来禁用显式的System.gc()

我强烈建议阅读Java HotSpot Garbage Collection中提供的文档,以获取更深入的垃圾回收详细信息。


我的Android应用程序总是抛出OutOfMemoryException异常。因此,我想在我的类的onDestroy函数中使用System.gc()来清除所有不需要的引用和对象。这是一个好的方法吗? - Sagar Devanga
2
@Sagar Devanga 手动调用垃圾回收器可能不会有所帮助。在抛出OOM之前,JVM已经尝试过进行垃圾回收以释放内存。 - Scrubbie

12

System.gc() 是由虚拟机(VM)实现的,它的具体功能取决于具体的实现。例如,实现者可能只是返回并不执行任何操作。

至于何时发出手动垃圾回收,唯一需要这样做的情况是当你放弃了一个包含大量小集合的大集合 - 比如一个 Map<String,<LinkedList>> - 并且你想尝试在那时尽快处理性能问题,但大部分情况下,你不用担心。垃圾回收器通常比你更聪明。


8
如果您使用直接内存缓冲区,即使您的直接内存不足,JVM也不会为您运行GC。如果您调用ByteBuffer.allocateDirect()并且出现OutOfMemoryError错误,则可以在手动触发GC后发现此调用是正常的。

你是说System.gc()只回收直接分配的内存,而JVM通常不会(在抛出OOM之前)。因为我认为这是错误的。显式GC后分配可能成功的唯一原因是额外的时间和更高的可能性,在堆中有一个带有finalizer的对象被释放(并释放一些直接分配的对象)。 - eckes
在最新版本的Java 6中,allocateBuffer中的代码在抛出OutOfMemoryError之前总是会运行System.gc()。在finalization代码中,它有一个Cleaners的快捷方式,用于直接内存使用。即使如此,如果您快速创建直接内存区域,仍然可能会出现OOME。解决方案是不要这样做,并尽可能重复使用您的直接内存区域。 - Peter Lawrey
1
谢谢,这更符合我的经验。所以我猜答案只对旧版本的Java正确。 - eckes

5

4

垃圾回收 在桌面/笔记本电脑/服务器中运行Java编写的软件时非常好用。在Java中,您可以调用System.gc()Runtime.getRuntime().gc()

请注意,这些调用都不能保证实现任何操作。它们只是向jvm建议运行垃圾回收器。是否运行GC取决于JVM。所以简短的答案是:我们不知道它何时运行。更长的答案是:如果JVM有时间,它会运行gc。

我认为,Android也是一样的。然而,这可能会减慢您的系统速度。


JVM不一定会在有时间的时候运行垃圾回收。当伊甸园内存已满时,JVM也会运行垃圾回收。 - abdelmouheimen

2

如果你想知道是否调用了System.gc(),在新的Java 7更新4中,你可以获得JVM进行垃圾回收时的通知。

我不确定GarbageCollectorMXBean类是在Java 7更新4中引入的,因为我在发布说明中找不到它,但我在javaperformancetuning.com网站上找到了这些信息。


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