有没有一种方法可以在Java中安排完整的垃圾收集?

18

我有一个按照24x6的时间表运行的应用程序。目前,在运行了几天之后,会自动执行一次Full GC(全堆垃圾回收),通常在一天中繁忙的时间段进行,这会对用户响应时间产生负面影响。

我想要做的是强制执行Full GC - 可能是每天午夜期间,即非常低的使用率时间 - 以防止它在白天发生。我尝试过System.gc(),但它似乎不能保证何时进行Full GC,甚至是否进行。有什么方法可以做到这一点吗?

版本信息:

Java(TM) SE Runtime Environment (build 1.6.0_11-b03)
Java HotSpot(TM) Server VM (build 11.0-b16, mixed mode)

此外,

  • Minor GC每10-15秒运行一次。但这些并没有释放足够的RAM来使应用程序通过整个星期。当进行Full GC时,近50%的堆被释放。
  • 与上述内容相关,调用System.gc()似乎并不意味着下一次GC将是所需的Full形式,以释放那些大块的内存。是的,它已启用(或未禁用,具体取决于如何阅读-XX选项)。
  • 我已经尝试了几种CMS GC设置,这大大有所帮助,但并没有解决问题。最初,它每周会抛出两到三次OOM(内存溢出)。
  • 我想停止无休止的循环:
    • 不断添加到堆空间 - 只能持续一段时间
    • 不断调整和测试GC设置 - 已远远超过收益递减点
  • 我不想像处理NT机器那样每晚都强制关闭。夜间仍有活跃的用户会话,并且关闭应用程序将意味着丢失会话数据。

更具体地说,我正在寻找更多的技巧来确保Full GC将发生,而不是简单地调用运行它的方法/函数。

目前我正在研究 CMS 使用的百分比阈值的修改,以确定何时需要进行 Full GC。

感谢任何帮助。


一般来说,没有办法强制进行垃圾回收 - 你所能做的只是“建议”进行垃圾回收。尽管一些服务器JVM可能有额外的旋钮可以调整。(一个真正实现并发GC的JVM才是解决您问题的真正方案。) - Hot Licks
请参见https://dev59.com/FXM_5IYBdhLWcg3wNgKY。 - Martin Dinov
你是否在使用System.gc()时遇到过任何问题,或者你的判断是基于文档?除非明确禁用-XX选项,否则在我的实践中,System.gc()是相当可靠的。 - Alexey Ragozin
1
值得一提的是,人们说这个问题是重复的,但它并没有标记答案。 - Jim Black
4个回答

21

jmap -histo:live <PID> 命令会在查找所有活动对象的“副作用”中强制执行Full GC。您可以计划在非工作时间回收JVM进程。

您的JVM版本为build 1.6.0_11-b03,相当古老,但是jmap应该支持所有1.6 HotSpot JVMs。


这可能是最好的答案。在半夜安排一个cron作业来运行此命令。 - MikeG

12

不。

System.gc() 建议 GC 进行收集。

此外,在安静期间可能生成的垃圾非常少,这就是为什么调用 System.gc() 没有太大作用的原因。

在高峰期,可能有更多的活动,因此生成的垃圾更多 - 因此需要进行收集。

显然,你不能以那种简单的方式推迟收集。JVM 将在需要时进行收集。

你需要考虑调整 GC - 如果出现停止世界的情况,那么你就有问题了。在现代服务器 JVM 上,这种情况不应该发生。

你应该研究一下 CMS 收集器的调整方法 - 是关于 Java 中 GC 系统基础知识的相当不错的文章。在 Java 7 中,有新的 G1GC,也许更好也可能更差。

你应该找到一种方法来模拟负载条件并尝试不同的 GC 参数,CMS GC 有许多调整参数,配置它有点像黑魔法...

是一篇更详细的关于 GC 调整和基准测试的文章。


1
“我不确定我是否理解 - 完整的GC是完整的GC,将GC自上次完整GC以来累积的所有垃圾。” “在安静期间可能生成的垃圾非常少,这就是为什么调用System.gc()没有太大作用的原因。” - assylias
@assylias 我的意思是JVM不会让垃圾堆积 - 垃圾回收将自动进行。因此,在空闲期间调用GC不会阻止在繁忙期间调用GC。我想一个类比就是在非高峰期运行更多火车并不能减轻高峰时段的负载。 - Boris the Spider
1
不,但在高峰前把所有人都带到他们想去的地方会有所帮助。但这个比喻并不适用。想想在许多客人到达之前清空垃圾箱。肯定会持续更长时间,直到再次填满,如果你非常幸运,它可能会经受住整个聚会的考验。 - maaartinus
1
@BoristheSpider 区别在于正常运行时间。有两个主要因素:本地峰值和总运行时间。如果完整的GC仅在连续工作的第二天发生,则在午夜运行完整的GC具有意义:它将重置jvm状态到初始状态。这可以通过在午夜重新启动jvm而无需强制执行完整的GC来修复。但是这很丑陋,像jvm这样出色的运行时应该有一些手段来清除其状态而无需重新启动。 - ayvango
1
@ayvango 完整的GC应该永远不会发生。这是JVM在正常GC(CMS,G1GC等)未能清除足够内存时的最后手段。如果您的应用程序遇到停止世界GC,则可能存在JVM配置问题或内存泄漏 - 无论哪种情况,都需要解决。在特定时间强制进行完整的GC是Java开发人员的最后手段,他们不知道JVM实际工作原理。 - Boris the Spider
JVM并不是魔法。总有办法可以无限增长垃圾而不泄漏内存。无论你的启发式算法多么聪明,总有失败的可能性。而这些条件可能是由随机过程形成的。 - ayvango

3

我认为可以这样做 - 为你的安静时间安排一个进程,执行一些你认为会触发垃圾回收的操作。

占用大量内存。分配大量对象并使用弱引用来跟踪它们 - 只需在安静时间做一些应该触发垃圾回收的操作即可。

确保你有一些逻辑来检测垃圾回收并停止进程。


1
无法强制立即进行垃圾回收,因为垃圾收集器是非确定性的。

5
"non-deterministic"可能是一个不恰当的词选择。垃圾回收将会在JVM算法规定的时间准确地运行。如果我花了几个星期研究JVM和OP的源代码,我可以根据OP的垃圾生成和JVM的GC算法确定垃圾回收将运行的时间。那当然是确定性的。仅仅因为做这项研究是浪费时间,并不意味着它无法完成。 - Brian S

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