为什么Java要等待这么长时间才能运行垃圾回收器?

我正在构建一个Java Web应用程序,使用Play!框架。我在 playapps.net 上托管它。我一直对提供的内存消耗图表感到困惑了一段时间。下面是一个示例:

Heap Memory

该图表来自一段一致但名义上的活动时期。我没有做任何事情来触发内存中的衰减,所以我推测这是因为垃圾回收器在几乎达到允许的内存消耗时运行。

我的问题:

  • 对于我来说,假设我的应用程序没有内存泄漏,因为垃圾回收器在运行时似乎正确回收了所有内存,这是否公平?
  • (从标题)为什么java要等到最后一秒才能运行垃圾回收器?我看到性能显着下降,因为内存消耗增长到图表的前四分之一。
  • 如果我上面的断言是正确的,那么我该如何解决这个问题?我读过的关于SO的其他帖子似乎反对调用,从中立(“这只是运行GC的请求,所以JVM可能会忽略你”)到完全反对(“依赖的代码从根本上被破坏了”)。还是我在这里偏离了基础,我应该在我自己的代码中寻找导致这种行为和间歇性性能损失的缺陷?System.gc()System.gc()

更新
我已经开启了关于 PlayApps.net 的讨论,指出这个问题并在这里提到一些要点;具体@Affe关于完整 GC 设置的非常保守的注释,以及@G_H关于初始和最大堆大小设置的注释。

这是讨论的链接,尽管很遗憾你需要一个playapps帐户才能查看它。

当我得到反馈时,我会在这里报告反馈;非常感谢大家的回答,我已经从他们身上学到了很多东西!

分辨率
Playapps支持,仍然很棒,对我没有太多建议,他们唯一的想法是,如果我广泛使用缓存,这可能会使对象存活的时间超过需要的时间,但事实并非如此。我仍然学到了很多东西(哇噢!),我给了@Ryan阿莫斯绿色支票,因为我接受了他每半天打电话的建议,现在工作正常。System.gc()


答案 1

任何详细的答案都将取决于您使用的垃圾回收器,但是有些东西在所有(现代,sun/oracle)GC中基本相同。

每次你看到图表中的使用情况下降时,那就是垃圾回收。释放堆的唯一方法是通过垃圾回收。问题是有两种类型的垃圾回收,次要和完整。堆分为两个基本的“区域”。年轻且终身制。(现实中还有更多的子组。任何在Young中占用空间并且当次要GC出现以释放一些内存时仍在使用的东西,都将被“提升”为终身制。一旦某些东西跃升为终身制,它就会无限期地存在,直到堆没有可用空间并且需要完整的垃圾回收。

因此,对该图的一种解释是,您的年轻一代相当小(默认情况下,它可能占某些JVM上总堆的相当小的百分比),并且您在相对很长一段时间内保持对象“存活”的时间。(也许您在Web会话中保留了对它们的引用?因此,您的对象在垃圾回收中“幸存下来”,直到它们被提升到永久空间,在那里它们无限期地停留,直到JVM真正从内存中恢复良好。

同样,这只是适合您拥有的数据的一种常见情况。需要有关JVM配置和GC日志的完整详细信息才能真正确定发生了什么。


答案 2

Java在必须运行垃圾清理器之前不会运行垃圾清理器,因为垃圾清理器会减慢很多速度,不应该经常运行。我认为您可以更频繁地安排清洁,例如每3小时一次。如果应用程序从不消耗满内存,则没有理由运行垃圾清理程序,这就是为什么Java仅在内存非常高时才运行它的原因。

所以基本上,不要担心别人说什么:做最有效的事情。如果您发现在 66% 内存下运行垃圾清理程序可以提高性能,请执行此操作。