System.gc()什么时候做什么
我知道垃圾收集在Java中是自动的。 但是我明白,如果在代码中编写System.gc()
,那么Java VM可能会或可能不会在运行时决定在此时执行垃圾回收。 这是如何工作的? 当VM看到一个System.gc()
时,决定做什么(或不这样做)什么基础/参数? 在这种情况下,有没有例子把它放在代码中是个好主意?
实际上,它通常决定做垃圾回收。 答案的变化取决于很多因素,比如你正在运行哪个JVM,它处于哪种模式,以及使用哪种垃圾收集algorithm。
我不会在你的代码中依赖它。 如果JVM将抛出一个OutOfMemoryError,那么调用System.gc()将不会停止它,因为垃圾收集器会在它达到极限之前尝试释放尽可能多的东西。 我唯一一次看到它在实践中使用的是在IDE中,它附加到一个用户可以点击的button,但即使在那里,它也不是非常有用。
我可以想到,调用System.gc()的唯一例子是分析应用程序以search可能的内存泄漏。 我相信configuration文件在拍摄内存快照之前调用这个方法。
你不能控制Java中的GC – VM决定。 我从来没有遇到需要 System.gc()
的情况 。 由于System.gc()
调用只是简单地build议虚拟机执行垃圾收集,而且它也会执行完整的垃圾收集(在多代堆中的新一代和新一代),那么实际上会导致更多的 cpu周期被消耗必要。
在某些情况下,向虚拟机build议完成一个完整的集合是有意义的,因为您可能知道应用程序将在重启后的几分钟内处于空闲状态。 例如,在应用程序启动过程中,大量临时对象的初始化之后(即,我只caching了一个TON的信息,而且我知道一分钟左右我不会得到太多的活动)。 想象一下像Eclipse这样的IDE开始 – 它初始化很多,所以也许在初始化之后立即做一个完整的gc是有意义的。
Java语言规范不保证当您调用System.gc()
时,JVM将启动GC。 这就是“这个时候可能会或可能不会决定做GC的原因”。
现在,如果您查看OpenJDK源代码 (Oracle JVM的骨干),您将看到对System.gc()
的调用确实启动了一个GC循环。 如果您使用其他JVM,例如J9,则必须检查其文档以找出答案。 例如,Azul的JVM有一个连续运行的垃圾收集器,因此对System.gc()
的调用将不会执行任何操作
还有一些答案提到在JConsole或VisualVM中启动一个GC。 基本上,这些工具可以远程调用System.gc()
。
通常,您不希望从代码开始垃圾回收循环,因为它会随应用程序的语义混淆。 你的应用程序做一些业务的东西,JVM负责内存pipe理。 你应该把这些问题分开(不要让你的应用程序做一些内存pipe理,专注于业务)。
但是,对System.gc()
的调用可能是可以理解的。 考虑一下,例如,microbenchmarks。 没有人希望在微基准标记中间发生GC循环。 因此,您可能会在每次测量之间触发GC循环,以确保每次测量都从空的堆开始。
如果您调用System.gc()
则需要非常小心。 调用它可能会给应用程序添加不必要的性能问题,并且不能保证实际执行收集。 实际上可以通过java参数-XX:+DisableExplicitGC
禁用显式System.gc()
。
我强烈推荐阅读Java HotSpot垃圾收集的文档,以获取有关垃圾收集的更多深入细节。
如果使用直接内存缓冲区,则即使在直接内存不足的情况下,JVM也不会为您运行GC。
如果你调用ByteBuffer.allocateDirect()
,你会得到一个OutOfMemoryError,你可以在手动触发一个GC之后发现这个调用是正常的。
System.gc()
由VM实现,它所做的是特定于实现的。 例如,实现者可以简单地返回,什么也不做。
至于什么时候发布手工收集,唯一一次你可能想要这样做的时候,是当你放弃一个包含大量较小的集合的大集合 – 一个Map<String,<LinkedList>>
,并且你想要尝试并尽可能快地进行打击,但大多数情况下,您不应该担心。 GC比你更了解 – 可悲的是 – 大部分时间。
大多数JVM将启动GC(取决于-XX:DiableExplicitGC和-XX:+ ExplicitGCInvokesConcurrent开关)。 但是为了稍后能够实现更好的实现,规范还没有很好的定义。
该规范需要澄清: 错误#6668279:(规格)System.gc()应表明,我们不build议使用,不保证行为
在内部,gc方法被RMI和NIO使用,它们需要同步执行,目前正在讨论中:
错误#5025281:允许System.gc()触发并发(而不是停止)全集合
在花费时间来testing各种垃圾收集设置方面有很多需要说的,但是正如上面提到的那样,这样做通常是没有用的。
我目前正在研究一个涉及内存有限的环境和相对大量数据的项目 – 有一些大的数据将我的环境推到极限,即使我能够减less内存使用那在理论上它应该工作得很好,我仍然会得到堆空间的错误—详细的GC选项告诉我,它试图垃圾收集,但无济于事。 在debugging器中,我可以执行System.gc(),并确保足够的内存可用…没有多less额外的,但足够的。
因此,我的应用程序调用System.gc()的唯一时间就是当它要进入代码段,在那里处理数据所需的大缓冲区将被分配,并且对可用空闲内存的testing表明我不是保证有它。 特别是,我正在寻找一个1GB的环境,其中至less300MB被静态数据占用,其中大部分非静态数据是执行相关的,除非正在处理的数据恰好在100-200 MB来源。 这是自动数据转换过程的一部分,所以数据在长时间内都存在相对较短的时间。
不幸的是,尽pipe关于调整垃圾收集器的各种选项的信息是可用的,但是它似乎在很大程度上是一个实验过程,而理解如何处理这些特定情况所需的较低层次细节并不容易获得。
所有这一切,即使我使用System.gc(),我仍然继续调整使用命令行参数,并设法提高我的应用程序的整体处理时间相当大的数量,尽pipe无法克服通过与更大的数据块进行合作而构成的绊脚石。 这就是说,System.gc()是一个工具….一个非常不可靠的工具,如果你不小心你如何使用它,你会希望它不工作更多。
运行显式GC的时候,我想不出一个具体的例子。
一般来说,运行显式的GC实际上会造成更多的伤害,而不是更好,因为显式的gc会触发完整的集合,当它穿过每个对象时会花费更长的时间。 如果这个显式的gc最终被重复调用,那么很容易导致应用程序的缓慢,因为大量的时间花在运行完整的GC上。
或者,如果用堆分析器遍历堆,并且怀疑某个库组件要调用显式GC,则可以将它closures,将gc = -XX:+ DisableExplicitGC添加到JVM参数中。
通常情况下,虚拟机会在抛出OutOfMemoryException之前自动进行垃圾收集,因此添加一个明确的调用应该不会有什么帮助,除非它可能会将性能降低到一个较早的时间。
不过,我想我遇到了一个可能相关的案例。 我不确定,因为我还没有testing它是否有任何作用:
当你记忆映射一个文件时,我认为当足够大的内存块不可用时,map()调用会抛出一个IOExceptionexception。 在map()文件之前的垃圾收集可能有助于防止这种情况,我想。 你怎么看?
我们永远不能强制垃圾收集。 System.gc只是提示vm进行垃圾收集,但是,真正的机制运行的时间,没有人知道,这是由JSR规范说明的。
简而言之:
参数是依赖于VM的。
示例用法 – 无法为运行时/生产应用程序考虑,但对于某些性能分析工具,如调用
// test run #1 test(); for (int i=0; i<10; i++) { // calling repeatedly to increase chances of a clean-up System.gc(); } // test run #2 test();
如果您想知道是否调用了System.gc()
则可以在JVM执行垃圾收集时使用新的Java 7更新4获取通知。
虽然我不能100%确定GarbageCollectorMXBean类是在Java 7 update 4中引入的,因为我在发行说明中找不到它,但是我在javaperformancetuning.com站点中find了这些信息
在Bruce Eckel的Java中思考,显式System.gc()调用的一个用例就是当你要强制结束时,即调用finalize方法。
而system.gc工作,它会停止世界:所有的响应被停止,所以垃圾收集器可以扫描每个对象,以检查是否需要删除。 如果应用程序是一个Web项目,所有请求都会停止,直到gc完成,这将导致您的Web项目不能在一个分娩工作。
Garbage Collection
在Java
是好的,如果我们正在执行Desktop / laptop / server中的java编码的软件。 您可以在Java
调用System.gc()
或Runtime.getRuntime().gc()
。
请注意,这些电话都不能保证做任何事情。 他们只是jvm运行垃圾收集器的build议。 无论是否运行GC,它都在JVM上。 所以,简短的回答:我们不知道什么时候运行。 较长的答案:如果有时间,JVM将运行gc。
我相信,这同样适用于Android。 但是,这可能会降低您的系统。