System.gc()什么时候做某事?


118

我知道Java中的垃圾回收是自动化的。但是我知道,如果您调用System.gc()代码,那么JVM可能会或可能不会决定此时执行垃圾回收。这究竟如何工作?JVM看到时会根据什么基础/参数确切地决定执行(或不执行)GC System.gc()

在这种情况下,有任何示例是个好主意吗?


4
一个概念太复杂,无法在此处进行详细介绍,但本文对您有帮助:chaoticjava.com/posts/how-does-garbage-collection-work
GEOCHET

确切的行为取决于JVM,即取决于实现。
托尔比约恩Ravn的安徒生

Answers:


59

实际上,它通常决定进行垃圾回收。答案取决于很多因素,例如您正在运行的JVM,它处于哪种模式以及所使用的垃圾收集算法。

我不会在您的代码中依赖它。如果JVM即将抛出OutOfMemoryError,则调用System.gc()不会停止它,因为垃圾收集器将尽力释放它,直到达到极限为止。我唯一看到它在实践中使用过的是在IDE中,它附加到用户可以单击的按钮上,但是即使在那儿它也不是很有用。


3
您可以在jconsole中强制进行垃圾收集
Benedikt Waldvogel

25
@bene你真的可以“强迫”吗?jconsole只是调用System.gc()吗?
John Meagher's

4
我会System.gc()在视频游戏的加载屏幕中使用。那时,我真的不在乎该呼叫是否清除了所有可能清除的内容,或者什么也不做。我会,不过,喜欢它,而不是在游戏的载入画面时,“走向循环使用的对象付出的努力。”
KROW

30

我可以想到的唯一示例是在分析应用程序以搜索可能的内存泄漏时调用System.gc()的意义。我相信分析器会在拍摄内存快照之前调用此方法。


4
是的,那也是我所做的。例如,Runtime.freeMemory()返回的值仅在System.gc()之后才真正有意义,因为通常您想知道有多少内存被无法收集的实例阻塞。当然,仅用于调试/内存分析,决不用于生产。
sleske,2009年

w,对其他许多场景也有好处。例如,假设您有一个了解生命周期的模式。在onStop()中,如果实例为null,则最好调用System.gc()来帮助它。
portfoliobuilder

26

Java语言规范不保证您调用JVM时将启动GC System.gc()。这就是“此时可能决定是否进行GC”的原因。

现在,如果您查看OpenJDK源代码(它是Oracle JVM的骨干),您将看到对的调用System.gc()确实开始了一个GC周期。如果使用其他JVM(例如J9),则必须查看其文档以找出答案。例如,Azul的JVM有一个连续运行的垃圾收集器,因此调用System.gc()不会做任何事情

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

通常,您不想从代码开始垃圾回收周期,因为它会弄乱应用程序的语义。您的应用程序执行一些业务工作,JVM负责内存管理。您应该将这些关注点分开(不要让您的应用程序执行某些内存管理,而要专注于业务)。

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


26

您无法控制Java中的GC-由VM决定。我从来没有穿过其中的情况下运行System.gc()需要。由于System.gc()调用只是表明VM做垃圾回收,同时也做了完全的垃圾收集(新老两代在多代堆),那么它实际上可能造成更多的CPU周期比必要的消耗。

在某些情况下,可能会建议VM立即进行完整收集,因为您可能知道应用程序将在接下来的几分钟内处于空闲状态,然后再进行繁重的操作。例如,在应用程序启动期间初始化了许多临时对象之后(即,我只是缓存了大量信息,并且我知道一分钟左右不会有太多活动)。考虑一下诸如eclipse启动之类的IDE,它进行了大量的初始化工作,因此也许在初始化之后立即进行一次完整的gc有意义。


17

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

我强烈建议您阅读Java HotSpot Garbage Collection中提供的文档,以获取有关垃圾收集的更多详细信息。


我的Android应用程序总是向我抛出OutOfMemoryException。所以我想在类的onDestroy函数中使用System.gc()清除所有不需要的引用和对象。这是一个好方法

2
@Sagar Devanga按照您的描述手动调用gc不太可能有所帮助。在抛出OOM之前,JVM将已经尝试进行垃圾回收以释放内存
Scrubbie 2015年

10

System.gc()由VM实施,它的作用是特定于实施的。例如,实现者可以简单地返回而不做任何事情。

至于何时发布手动收集,您唯一想做的是,当您放弃包含大量较小收集的大型收集Map<String,<LinkedList>> (例如,a) ,并且想要尝试获得最佳性能时,在那里,但是在大多数情况下,您不必为此担心。不幸的是,GC多数时候比您更了解。


8

如果您使用直接内存缓冲区,那么即使您的直接内存不足,JVM也不会为您运行GC。

如果调用,ByteBuffer.allocateDirect()并且收到OutOfMemoryError,则可以在手动触发GC后发现此调用很好。


您是在说System.gc()收集直接分配,而JVM通常不收集(在抛出OOM之前)。因为我认为那是错误的。显式GC后可能导致分配的唯一原因是释放了终结器蜂(并释放了一些直接分配的对象)的额外时间和堆内对象的更多可能性。
eckes 2012年

在最新版本的Java 6中,allocateBuffer中的代码将始终在引发OutOfMemoryError之前运行System.gc()。在完成代码中,它具有Cleaners直接存储器使用的捷径。即终结器线程不会释放它,因为这可能太慢。即使这样,如果要特别快地创建直接存储区域,也可能会获得OOME。解决方案是不要这样做,并在可能的地方重新使用直接存储区域。
彼得·劳瑞

1
谢谢,这听起来更像是我的经历。因此,我猜答案仅对较早的Java版本是正确的。
eckes 2012年

5

大多数JVM将启动GC(取决于-XX:DiableExplicitGC和-XX:+ ExplicitGCInvokesConcurrent开关)。但是,规范的定义不太明确,以便以后可以更好地实现。

规范需要澄清:错误#6668279 :(规范)System.gc()应该表明我们不建议使用也不保证行为

RMI和NIO在内部使用gc方法,它们需要同步执行,这是当前正在讨论的问题:

错误#5025281:允许System.gc()触发并发(而不是世界末日)完整集合


3

Garbage CollectionJava如果我们要在Desktop / laptop / server中执行用Java编码的软件,则在方面很有用。您可以致电System.gc()Runtime.getRuntime().gc()Java

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

我相信,Android同样适用。但是,这可能会降低您的系统速度。


如果有时间,JVM将运行gc:并非总是如此,当Eden内存已满时,Jvm可以运行gc。
abdelmouheimen

2

通常,VM会在抛出OutOfMemoryException之前自动进行垃圾回收,因此添加显式调用无济于事,因为它可能会将性能降低的时间移到更早的时刻。

但是,我认为我遇到了一个可能与之相关的案例。我不确定,因为我还没有测试它是否有效果:

当您对文件进行内存映射时,我相信当没有足够大的内存块时map()调用会引发IOException。我认为,map()文件之前的垃圾回收可能有助于防止这种情况。你怎么看?


2

我们永远不能强迫垃圾收集。System.gc仅建议使用vm进行垃圾回收,但是,实际上该机制什么时候运行,没人知道,这是JSR规范所指出的。


2

花很多时间测试各种垃圾收集设置要说很多,但是如上所述,这样做通常没有用。

我目前正在从事一个项目,该项目涉及内存受限的环境和相对大量的数据-有一些大数据将环境推向极限,即使我能够降低内存使用量也是如此从理论上讲应该可以正常工作,但我仍然会遇到堆空间错误-冗长的GC选项向我表明它正在尝试进行垃圾收集,但无济于事。在调试器中,我可以执行System.gc()并确保足够多的内存可用……没有很多额外的内存,但是足够了。

因此,我的应用程序唯一一次调用System.gc()的时候就是要进入代码段,在该段中将分配用于处理数据的大缓冲区,并且对可用内存的测试表明我没有保证拥有它。特别是,我正在研究一个1gb的环境,其中静态数据至少占用300mb,其中大部分非静态数据与执行相关,除非正在处理的数据恰好至少为100-200 MB,来源。这都是自动数据转换过程的一部分,因此从长远来看,所有数据都存在相对较短的时间。

不幸的是,尽管可以获得有关用于调整垃圾收集器的各种选项的信息,但这似乎很大程度上是一个实验过程,并且不容易获得了解如何处理这些特定情况所需的较低级别的细节。

综上所述,即使我使用System.gc(),我仍然继续使用命令行参数进行调优,尽管无法克服问题,但仍设法将应用程序的整体处理时间缩短了相当多的时间。通过处理较大的数据块而构成的绊脚石。话虽这么说,System.gc()是一个工具。。。一个非常不可靠的工具,如果您不小心使用它,则希望它不会经常工作。



0

我想不出一个具体的例子来运行显式GC很好。

通常,运行显式GC实际上会带来弊大于利,因为显式gc会触发完整的收集,而收集通过每个对象所花费的时间要长得多。如果此显式gc最终被重复调用,则可能会导致应用程序运行缓慢,因为花费大量时间运行完整的GC。

或者,如果使用堆分析器检查堆,并且怀疑库组件正在调用显式GC,则可以将其关闭,方法是:在JVM参数中添加gc = -XX:+ DisableExplicitGC。


2
我们调用System.gc()尝试关闭我们的MappedByteBuffer对象,否则这些对象不会关闭,因此即使我们在对象上调用“ close()”,也无法用于其他进程或文件截断。不幸的是,它仍然不能总是关闭它们。
蒂姆·库珀

0

布鲁斯·埃克尔(Bruce Eckel)用Java来思考,显式System.gc()调用的一个用例是当您要强制完成时,即对finalize方法的调用。


注意:有一种强制完成运行的方法。(问题实际上不在运行终结器中,而是认识到对象可以被终结,因为它未被引用)
eckes 2012年

-1

当system.gc工作时,它将停止工作:所有响应都停止了,因此垃圾收集器可以扫描每个对象以检查是否需要删除它。如果应用程序是Web项目,则所有请求都将停止,直到gc完成,这将导致您的Web项目无法正常运行。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.