記一次臨近上線程序發生OOM

記一次臨近上線程序發生OOM

故事背景

最近一直在趕着應用上線,基本已經封包準備上線了,誰都不想在這時間點上出差錯~java

當時應用已經上線pre,壓力測試已經經過,然而昨天下午測試組的同事忽然找到我,說個人應用沒有消費kafka的數據,其餘應用都已經同步消費了,搞得我一臉懵逼.web

首先先上Consul上看服務的狀況.發現pre上面除了個人應用,其餘的都還健在!!!!(內心有點方~)spring

趕忙連上服務器查看應用,發現日誌在停留在19號凌晨1點多.服務器

[2019-07-19 01:46:33] [WARN ] [dc16dc8a-79d8-4ec5-adca-3f7bdff7bb7d] [http-nio-6030-exec-7] [AbstractHandlerExceptionResolver.java:140] [org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver] : Resolved [org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.OutOfMemoryError: GC overhead limit exceeded]

而後趕忙重啓讓測試同事去驗證數據同步~session

排查問題

因爲服務器內存吃緊(8G),而後又須要部署好多個應用,默認啓動JVM參數設置得比較低(-Xms256m -Xmx512m -Xmn128m -XX:MaxPermSize=64m)~mvc

因爲沒有加上OOM dump參數,因此那時候也沒辦法去定位問題,只能是在本地將問題復現了.jvm

  1. 首先在本地設置一樣的JVM參數,再加上發生OOM dump的路徑
-Xms256m -Xmx512m -Xmn128m -XX:MaxPermSize=64m -XX:ErrorFile=G:/heap/dump/hs_err_pid%p.log  -XX:HeapDumpPath=G:/heap/dump -XX:+HeapDumpOnOutOfMemoryError

而後讓程序跑一段時間,程序果然復現了!!!同時在咱們設置好的發生OOM生成對應的堆文件.測試

[2019-07-20 11:01:50] [ERROR] [c8dc279e-1e7e-4a1a-b646-0203f23e7ba6] [http-nio-6030-exec-116] [AdviceController.java:48] [com.ost.micro.scheduler.strategy.controller.AdviceController] : {}
org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.OutOfMemoryError: GC overhead limit exceeded
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1006)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:877)

讓咱們看一下如今的JVM堆圖: JVM堆this

堆內存一直不回收,致使了OOM.spa

打開Eclipse Memory Analyzer 分析生成的dump文件.咱們發現org.drools.core.impl.KnowledgeBaseImpl佔據了72%!!!

分析

解決問題

工做緣由,接觸到了drools這個規則引擎,上面發現的問題明顯就是使用drools整出來的,因爲程序裏面KieBase設置的是單例,那麼問題應該不是出在它身上。

先來看一下使用drools的代碼~

public <T extends IRule> Integer execute(T t) {
    KieSession session = this.session();
    session.insert(t);
    int size = session.fireAllRules();
    if (Objects.nonNull(t.getIError())){
        throw new PayStrategyException(t.getIError());
    }
    return size;
}

此時我就在懷疑,是否是KieSession沒關閉致使的問題?看了一下獲取KieSession的描述~

/**
 * Creates a new {@link KieSession} using the default session configuration.
 * Don't forget to {@link KieSession#dispose()} session when you are done.
 *
 * @return created {@link KieSession}
 */

寫的很清楚,當執行完了以後,必須執行dispose()方法回收該session.修改代碼以下

public <T extends IRule> Integer execute(T t) {
    KieSession session = null;
    try {
        session = this.session();
        session.insert(t);
        int size = session.fireAllRules();
        if (Objects.nonNull(t.getIError())) {
            throw new PayStrategyException(t.getIError());
        }
        return size;
    } finally {
        if (Objects.nonNull(session)) {
            session.dispose();
        }
    }
}

設置一下JVM參數(-Xmx1344M -Xms1344M -Xmn448M -XX:MaxMetaspaceSize=256M -XX:MetaspaceSize=256M -XX:+UseConcMarkSweepGC -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses -XX:+CMSClassUnloadingEnabled -XX:+ParallelRefProcEnabled -XX:+CMSScavengeBeforeRemark), 從新跑一下JVM堆以下圖: JVM堆

寫在最後

因爲對Drools這個工做引擎不熟悉,因此差點搞出問題,看來仍是要多看看官方的文檔,記下這次事故提醒本身~

最後推薦一下生成JVM參數的網址http://xxfox.perfma.com/jvm/generate

相關文章
相關標籤/搜索