最近一直在赶着应用上线,基本已经封包准备上线了,谁都不想在这时间点上出差错~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