8

我的简单目标:监控 Java 应用程序的内存使用情况,以便在应用程序危险地接近抛出OutOfMemoryError.

是的,说起来很简单,但想出一个正确的解决方案似乎很复杂。一些复杂的因素是:

  • 有不同的堆区域,每个区域都可以抛出OutOfMemoryError
    • permgen空间,它有自己的大小限制(通过设置-XX:MaxPermSize=
    • 整体堆空间(通过 设置-Xmx
  • VM 可能会在进行垃圾收集之前分配几乎所有的堆。如果应用程序使用了大量的软引用,那么事实上这肯定会发生。因此,仅高堆分配百分比并不意味着应用程序即将抛出OutOfMemoryError.
  • 如果能System.gc()保证 VM 会回收所有可能的可回收对象(未引用和/或弱引用对象),那就太好了,但事实并非如此。所以调用System.gc()thenRuntime.freeMemory()是不可靠的。
  • 排队等待完成的对象占用内存,但(通常)在完成后被释放。因此,终结器线程是否已经到达它们会影响(明显的)内存使用(VM 是否在抛出 OOM 之前将终结器作为最后的独立行为运行?看起来不像。
  • 本机代码也会占用内存,过多使用它会导致 OOM(这在我的特定应用程序中不太可能出现,但确实给整体情况增加了另一个复杂性)。

那么,什么是回答这个问题的好而可靠的方法:我的 Java 应用程序是否会抛出OutOfMemoryError?

换句话说,假设应用程序版本 X 运行良好并且没有内存泄漏,但版本 X + 1 有一个缓慢的无法识别的内存泄漏。我希望在版本 X + 1 抛出 之前收到此监控的警报OutOfMemoryError,但我希望完全相同的监控不会对版本 X 给出误报。在设置此监控时可能需要进行一些调整 - 没关系.

一个可能的答案可能是这样的:在过去 N 次“完整”GC 运行中,在 GC 运行后立即堆利用率的最大值是多少?如果此值超过总分配内存的 X%,则发出警报。

这个想法是用简单的数字(如百分比,甚至是 LOW、MEDIUM 或 HIGH)来确定“应用程序内存使用情况”,然后监控这个值。

jstat命令提供了大量相关信息,问题在于将其归结为一个简单的答案,并避免由上面列出的复杂因素引起的误报(或否定)。

4

2 回答 2

7

如果您观察一个长时间运行的应用程序的内存图(例如,使用 jconsole 之类的工具收集),您会看到一个典型的锯齿模式:内存使用量上升,然后 GC 回到基线,然后它会上升再次。对于一个健康的应用程序,高峰和低谷在两条水平线上。但是,对于泄漏的应用程序,基线会攀升。这确实是您需要注意的:如果每个连续的 GC 都不如上一次有效,那么丹麦的某些东西就烂了。

于 2013-06-07T15:49:09.307 回答
0

在 Oracle 文档页面中搜索该术语Detecting Low Memory,并且Threshold Notifications您可以设计一些基于内置 MXBeans 的警报系统。垃圾收集似乎是至少一些指标收集的焦点。

于 2013-06-10T21:19:23.827 回答