我想为我的系统提供一种检测是否发生内存不足异常的方法。此练习的目的是通过 JMX 公开此标志并采取相应措施(例如,通过在监控系统上配置相关警报),否则这些错误几天内都不会被注意到。
简单的方法是为每个线程设置一个未捕获的异常处理程序,并检查引发的异常是否是
OutOfMemoryError
的实例并设置相关标志。然而,由于以下原因,这种方法并不现实:
Throwable
并将其据为己有。我看到的一种可能的场景是字节码操作(例如,在
OutOfMemoryError
之上附加某种方面),但是我不确定这是否是正确的方法,或者这通常是否可行。
我们启用了
-XX:+HeapDumpOnOutOfMemoryError
,但我不认为这是解决此问题的方法,因为它是为其他目的而设计的 - 并且在发生这种情况时它不提供 Java 回调。
有人做过吗?您将如何解决它或建议解决它?欢迎任何想法。
您可以使用内存不足警告系统;这个OutOfMemoryError警告系统可以是一个灵感。您可以配置一个侦听器,在超出某个内存阈值(例如 80%)后调用该侦听器 - 您可以使用此调用来开始采取纠正措施。
我们使用类似的东西,当组件的内存阈值达到80%时,我们暂停组件的服务,并开始清理操作;仅当使用的内存低于另一个可配置值阈值时,该组件才会返回。
我们启用了 -XX:+HeapDumpOnOutOfMemoryError,但我没有看到这个 作为这个问题的解决方案,因为它是为其他东西而设计的 - 当发生这种情况时,它不提供 Java 回调。
这个标志应该就是您所需要的。 将生成的堆转储文件的输出目录设置在您定期检查的某个已知位置。 回调对你来说没有任何用处。 如果内存不足,则无法保证回调代码有足够的内存执行! 您所能做的就是收集数据并使用外部程序来分析内存不足的原因。 任何在过程中恢复的尝试都可能会产生更大的问题。
字节码检测是可能的,但很难。 HPjmeter 监控工具能够预测未来的 OOM(带有警告)——但仅限于基于 HP-UX/Itanium 的系统。 您可以专用一个守护线程来计算进程中使用的内存,并在超出时触发警报,但实际上您并没有解决问题。
您可以使用静态 Thread.setDefaultUncaughtExceptionHandler.
捕获任何和所有
未捕获异常。当然,如果有人捕获所有 Throwables,这也无济于事。 (我不认为任何事情都会发生,尽管对于 OOME,我怀疑你会得到级联效应,直到有问题的
try
块之外的东西爆炸。)希望线程能够释放足够的内存,以便异常处理程序能够工作;当您尝试处理 OOM 错误时,它们确实会成倍增加。
我选择了一个稍微不同的选择。在我们的swing应用程序中,outofmemoryerror可能是由第三方库抛出的,也可能是在其他地方抛出的,但无论在哪里,最终似乎总是将OutOfMemoryError打印到System.err,因此我编写了以下代码。 这使得应用程序中通常不可见的 JPanel 在发生 OutofMemoryError 时显示,向用户解释发生了什么,这比应用程序只是挂起/崩溃要好得多。 它还将 System.err 打印到文件和控制台。
private void initErrorOutput() {
try {
File f = new File("errors.log");
boolean append = f.exists() && f.length() < 4*1024*1024;
FileOutputStream out = new FileOutputStream(f, append);
PrintStream old = System.err;
MyErrorStream mes = new MyErrorStream(out,old);
System.setErr(mes);
}catch(Exception ex) {
ex.printStackTrace();
}
}
private class MyErrorStream extends java.io.PrintStream{
PrintStream console;
private MyErrorStream(java.io.OutputStream out, PrintStream old) {
super(out);
console = old;
}
private void check(Object o) {
if (o == null) return;
if (o instanceof char[]) o = new String((char[])o);
String s = o.toString();
if (s.contains("java.lang.OutOfMemoryError")) {
System.out.println("\033[31m"+s+"\033[0m");
outofmemoryPanel.setVisible(true);
}
}
@Override
public void println(Object x) {
console.println(x);
super.println(x);
check(x);
}
}