我的简单目标:监控 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命令提供了大量相关信息,问题在于将其归结为一个简单的答案,并避免由上面列出的复杂因素引起的误报(或否定)。