如何读取运行时日志并针对JVM Out of Memory错误创建告警?
针对JVM OutOfMemoryError的告警与全局捕获方案
一、通过运行时日志检测并触发告警
- 日志采集+规则匹配:如果用ELK、Prometheus+Grafana这类监控栈,直接配置日志采集任务,设置匹配规则——只要日志中出现
java.lang.OutOfMemoryError关键词,就触发告警(比如Grafana的告警通知、ELK的Watcher告警)。小型项目也可以自己写简单脚本(Shell或Python),实时跟踪日志文件(类似tail -F的逻辑),用正则匹配关键词,匹配到就调用告警接口(发邮件、企业微信消息等)。 - JVM参数定向错误日志:通过JVM参数把OOM相关错误单独输出到指定文件,方便精准监控:
XX:+HeapDumpOnOutOfMemoryError:OOM时自动生成堆转储文件(用于事后排查)-XX:ErrorFile=./hs_err_pid_%p.log:把JVM错误日志写入指定路径,专门监控这个文件的新增内容,匹配OOM关键词更高效
二、全局捕获OutOfMemoryError的方案
OutOfMemoryError属于Error而非Exception,普通try-catch Exception无法捕获,需要针对性处理:
- 主线程全局捕获:在main方法最外层加try-catch,直接捕获Error:
public static void main(String[] args) { try { // 启动你的应用 startYourApp(); } catch (OutOfMemoryError oom) { // 触发告警逻辑,尽量轻量化 sendOOMAlert("主线程OOM:" + oom.getMessage()); oom.printStackTrace(); } catch (Throwable t) { // 兜底处理其他严重错误 t.printStackTrace(); } }
- 线程全局异常处理:主线程抓不到其他线程抛出的OOM,需要给线程设置未捕获异常处理器:
// 自定义处理器 class OOMExceptionHandler implements Thread.UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { if (e instanceof OutOfMemoryError) { sendOOMAlert("线程[" + t.getName() + "]发生OOM:" + e.getMessage()); } e.printStackTrace(); } } // 设置全局默认处理器,所有新创建的线程都会继承 Thread.setDefaultUncaughtExceptionHandler(new OOMExceptionHandler()); // 如果用线程池,要给线程工厂指定处理器 ExecutorService executor = Executors.newFixedThreadPool(5, r -> { Thread thread = new Thread(r); thread.setUncaughtExceptionHandler(new OOMExceptionHandler()); return thread; });
- 注意:有些OOM场景无法被捕获——比如JVM分配内存时直接崩溃、GC线程等系统线程抛出OOM,这类情况只能靠日志或监控检测。另外,捕获OOM后的告警逻辑要尽量简单,避免因再次申请内存导致问题恶化。
三、更稳妥的综合方案
建议把日志检测和全局捕获结合,再配合JVM内存监控提前预警:
- 用JMX采集堆内存、元空间等指标(比如通过jmx_exporter对接Prometheus),设置内存使用率阈值告警(比如使用率超90%发预警),提前避免OOM发生;
- 日志检测作为兜底,覆盖全局捕获不到的场景;
- 全局捕获则能在OOM发生时第一时间触发告警,减少延迟。
内容的提问来源于stack exchange,提问作者bateda
相关产品推荐
相关产品推荐

