首页 > 解决方案 > 如何在 Java 中强制进行第一代 GC?

问题描述

使用该System.gc()方法可以强制执行完整的 GC 运行。这是非常昂贵的。有没有只强制第一代GC的选项?我的应用程序当前在 Java 11 上运行。我正在考虑类似MemoryPoolMXBean.

背景:我想启动一个消耗大量内存的任务,如果可用内存低于所需内存,那么我想知道我是否可以毫无风险地启动它,或者我应该稍后尝试还是将缓存交换到磁盘。实际上,我想知道第一次 GC 后的空闲内存。我们的分析表明,这在大多数情况下(>99%)就足够了。

标签: javamemorygarbage-collectionmxbean

解决方案


简短的回答是没有支持的方法来做到这一点。它是System.gc()或什么都不是。

更长的答案:

我搜索了 JVM 源代码并找到了 C++ CollectedHeapAPI。(如果您感兴趣,它位于“src/hotspot/share/gc/shared/collectedHeap.hpp”中。)这是JVM 配置的堆和JVM 其余部分之间的内部接口。

如果您查看此 API,您可以看到 JVM 可以调用以触发垃圾收集的虚拟方法。事实证明,它们有 3 个,但它们都调用了完整的垃圾回收。因此,即使您尝试从使用 JNI 调用的本机代码中触发 GC,您最终还是会执行完整的 GC。

可以下载 OpenJDK 源代码,修改CollectedHeapAPI 和堆实现,以便触发新一代 GC,并构建具有所需功能的自定义 JVM。如果您遵守 OpenJDK “GPLv2 + Classpath exception”许可的条款,您甚至可以分发二进制文件。然而,这将是一项艰巨的任务。


但我认为你的问题被误导了。

触发 GC 几乎总是一个坏主意。作为一般规则,堆本身知道运行 GC 的最佳(最有效)时间,以满足您在 GC 调整选项中配置的目标。最好的策略是不干涉。只需让 JVM 处理时间。

根本问题是运行的大部分成本gc()都花在了处理非垃圾对象上。即收集器需要从一个空间复制到另一个空间的对象。留下的任何垃圾(通常)都会被清零。因此,运行收集最符合人体工程学的时间是垃圾与非垃圾的比率(在被收集的空间中)尽可能高的时候;即当它接近满时。

System.gc()干扰(通过调用)生产代码的唯一合理的理由是,如果应用程序有一些点,它知道低效是可以的。一个经典的例子是一个游戏,GC 暂停在正常游戏过程中是有害的,但在(例如)加载下一个“关卡”时它们是可以的。因此在加载关卡后触发 GC 可能是有益的。

从您的问题和评论中不清楚您通过强制 GC 运行来实现什么目标。但我的建议是阅读 Java GC 调优,设置 JVM 选项,然后将其留给 JVM 来管理堆。


推荐阅读