System.gc()何时执行某些操作?

我知道垃圾收集在 Java 中是自动化的。但是我理解,如果您在代码中调用 System.gc(),那么 JVM 可能会或可能不会决定在此时执行垃圾收集。这到底是怎么运作的?当 JVM 看到 System.gc()时,它是根据什么基础/参数来决定执行(或不执行) GC 的?

在这种情况下,有没有什么样的例子可以把它放到你的代码中呢?

156917 次浏览

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

在你的代码中我不会依赖它。如果 JVM 要抛出 OutOfMemory 错误,那么调用 System.gc ()不会停止它,因为垃圾收集器将尝试释放尽可能多的内容,以免到达极限。我唯一一次看到它在实践中使用是在 IDE 中,它被附加到一个用户可以点击的按钮上,但即使在那里它也不是特别有用。

System.gc()是由 VM 实现的,它所做的是特定于实现的。例如,实现者可以简单地返回并什么也不做。

至于何时发布手动集合,您可能希望这样做的唯一时间是当您放弃包含大量小集合的大型集合时—— a 例如,Map<String,<LinkedList>>——您想尝试在那里和那里进行性能测试,但是在大多数情况下,您不应该担心这个问题。GC 比你更清楚,很不幸,大多数时候。

在 java 中,您无法控制 GC ——由 VM 决定。我从没遇到过 System.gc()等于 需要的情况。因为一个 System.gc()调用只是建议 VM 执行一个垃圾收集,并且它也执行一个 FULL 垃圾收集(多代堆中的旧的和新的代) ,所以它实际上会导致 更多 CPU 周期被消耗而不必要。

在某些情况下,向 VM 建议立即进行完整的收集可能是有意义的,因为您可能知道应用程序将在接下来的几分钟内处于空闲状态,然后才会发生繁重的工作。例如,在应用程序启动期间初始化许多临时对象之后(例如,我刚缓存了一吨信息,我知道在一分钟左右的时间内不会有太多活动)。想象一下 Eclipse 启动这样的 IDE ——它进行了大量初始化工作,因此在初始化之后立即执行完整的 gc 是有意义的。

我想不出什么特定的例子适合运行显式 GC。

一般来说,运行显式 GC 实际上弊大于利,因为显式 GC 将触发完整的集合,这将在遍历每个对象时花费更长的时间。如果这个显式的 gc 最终被重复调用,它很容易导致应用程序运行缓慢,因为运行完整的 gc 需要花费大量时间。

或者,如果使用堆分析器遍历堆,并且怀疑某个库组件正在调用显式 GC,则可以关闭它,向 JVM 参数添加: GC =-XX: + DisableExplicity GC。

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

我强烈推荐阅读 Java HotSpot 垃圾收集中提供的文档,以获得关于垃圾收集的更深入细节。

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

大多数 JVM 将启动 GC (取决于-XX: DiableExplicity GC 和-XX: + Explicity GCInvokesConcurrent 开关)。但是为了以后能够更好地实现,规范的定义就不那么明确了。

说明书需要澄清: Bug # 6668279: (spec) System.gc ()应该表明我们不建议使用并且不保证行为

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

Bug # 5025281: 允许 System.gc ()触发并发(而不是停止世界)完整集合

通常,VM 会在抛出 OutOfMemory 异常之前自动执行垃圾收集,所以添加一个显式调用应该不会有什么帮助,除非它可能会将性能影响提前。

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

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

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

如果您调用 ByteBuffer.allocateDirect()并得到一个 OutOfMemory 错误,那么您可以在手动触发 GC 之后发现这个调用是正常的。

我们永远不能强制垃圾回收。Gc 只是建议使用 vm 进行垃圾收集,但是,没有人知道这个机制到底什么时候运行,这是 JSR 规范中规定的。

花时间测试各种垃圾收集设置有很多要说的,但是正如上面提到的,这样做通常没有用处。

我目前正在进行一个项目,涉及到一个内存有限的环境和一个相对大量的数据——有一些大块的数据将我的环境推到了极限,即使我能够降低内存使用量,从理论上来说它应该可以正常工作,我仍然会得到堆空间错误——详细的 GC 选项告诉我它正在尝试垃圾收集,但是没有用。在调试器中,我可以执行 System.gc () ,并且足够确定有“足够的”内存可用... 不是很多额外的内存,而是足够的内存。

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

不幸的是,虽然有关调优垃圾收集器的各种选项的信息是可用的,但这似乎在很大程度上是一个试验性的过程,并且不容易获得理解如何处理这些特定情况所需的底层细节。

尽管如此,即使我使用 System.gc () ,我仍然继续使用命令行参数进行调优,并且设法相对较大程度地提高了应用程序的整体处理时间,尽管无法克服处理较大数据块造成的障碍。也就是说,System.gc ()是一个工具... ... 一个非常不可靠的工具,如果您不注意如何使用它,您会希望它不会经常工作。

根据 Bruce Eckel 的《 Java 中的思考》 ,显式 System.gc ()调用的一个用例是当您想要强制终止时,即对 结束方法的调用。

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

如果您想知道是否调用了 System.gc(),可以使用新的 Java7更新4在 JVM 执行垃圾收集时获得通知。

但是,我不能100% 确定 垃圾收集器类是在 Java7更新4中引入的,因为我在发行说明中找不到它,但是我在 Java 性能优化.com 网站上找到了相关信息

如果我们在桌面/笔记本/服务器中执行 Java 编码的软件,那么 Garbage CollectionJava中是很好的。您可以在 Java中调用 System.gc()Runtime.getRuntime().gc()

请注意,这些调用都不能保证执行任何操作。它们只是建议 jvm 运行垃圾收集器。无论是否运行 GC,它都在 JVM 上。简而言之,我们不知道它什么时候运行。更详细的答案是: 如果 JVM 有时间运行 gc,它就会运行 gc。

我相信,同样的道理也适用于 Android。但是,这可能会降低你的系统运行速度。

Java 语言规范并不保证在调用 System.gc()时 JVM 将启动 GC。这就是为什么“可能会或可能不会在那个时候决定执行 GC”。

现在,如果您查看作为 Oracle JVM 主干的 OpenJDK 源代码,您将看到对 System.gc()的调用确实启动了 GC 循环。如果使用另一个 JVM,比如 J9,则必须检查它们的文档才能找到答案。例如,Azul 的 JVM 有一个连续运行的垃圾收集器,因此调用 System.gc()不会做任何事情

其他一些答案提到在 JConsole 或 VisualVM 中启动 GC。

通常,您不希望从代码开始一个垃圾收集周期,因为它会扰乱应用程序的语义。您的应用程序做一些业务工作,JVM 负责内存管理。您应该将这些关注点分开(不要让您的应用程序进行一些内存管理,而是将重点放在业务上)。

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