最近一直在趕着應用上線,基本已經封包準備上線了,誰都不想在這時間點上出差錯~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
-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堆圖: 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堆以下圖:
因爲對Drools這個工做引擎不熟悉,因此差點搞出問題,看來仍是要多看看官方的文檔,記下這次事故提醒本身~
最後推薦一下生成JVM參數的網址http://xxfox.perfma.com/jvm/generate