记一次临近上线程序发生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

相关文章
相关标签/搜索