实战解决生产环境OOM的问题

需要先来一波小小的吐槽,OOM真特么坑爹啊!!!

各种原因都有可能导致OOM!!而且如果根据程序抛出的异常来定位问题,无异于大海捞针!!

为了帮助大家避免弯路,分享一下自己处理OOM的过程和心得。

怀着开机混底薪的轻松心疼走进办公室沏了一杯茶,打开CSDN浏览大家的佳作。 

 

突然钉钉中出现很多吐槽的声音,原来运营同事陆陆续续接到很多用户的投诉,反应系统卡慢甚至503问题。妈的,愉快的一天从接到BUG开始走向结束。远程连接服务器查询Tomcat日志,吃惊的发现出现大量OOM的异常信息,而且各式各样,花样百出。

一脸闷逼的我觉得内存溢出应该属于项目经理处理的问题,但转念想到,白给的实战JVM调优经验,没理由转手送出去呀。而且总是逃避问题永远不可能独当一面。于是,硬着头皮上吧。

 基于目前用户吐槽的问题是访问不了,可日志又看不出什么有用信息,所以为了公司收益,还是先重启Tomcat保证用户使用吧。然后顺便找找有什么方法可以直观地看到JVM堆的实际使用量。灵光一闪,貌似以前学习JVM相关调优时,记忆中jdk自带的jconsle好像可以远程链接,然后就网上查找相关的信息(这个过程才坑爹,一万个博客一万种方案,就是没有适合自己的),在tocmat启动文件catalina.sh中加入如下命令:

JAVA_OPTS="-Djava.rmi.server.hostname=服务器的ID  -Dcom.sun.management.jmxremote.port=你要链接的端口(不被占用的端口) -Dcom.sun.management.jmxremote.rmi.port=你要链接的端口(不被占用的端口) -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"

我这种方式是不使用用户名和密码的方式,存在一定安全性问题,大家可以参考使用。

如果启动Tomcat出现hostname相关的异常,证明你需要在服务器上配置对应的hostname主机名(自行百度,很简单)。

注意在catalina.sh中JAVA_OPTS只能配置一次,否则下面的配置会覆盖上面的配置。

最终成功连接上服务器的tomcat,此起彼伏的CPU占用率和老年代堆内存使用量映入眼帘。

 

其中低谷属于我重启服务导致资源的释放,但可以看出释放资源之后CPU又马上火箭式的飙升,然后再重启吗?显然不是长久之计。所以我在tocmat启动文件catalina.sh中加入了OOM异常后,导出堆内存快照的参数。

JAVA_OPTS中加入
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=你希望导出的路径

果然,再次重启后,遇到OOM,我如愿得到了这个.hprof后缀的超大文件,通过压缩传输的方式放到本地,使用JProfiler打开堆内存。 

 

 

 很直观的看到这个对象在堆内存中占据了1G多,而且打开这个对象,发现其属性也是Long,ArrayList,HashMap,刚好和上图吻合。

 进而定位代码,发现历史代码中存在递归查询的方式,果然!有递归查询的地方就有雷!

 

此代码初衷应该是想获取此用户的所有下级用户,那么为什么会跳不出循环呢?一定是关系出现了环!

所以我有写了一段代码去测试系统用户表是否有环。 

    public ListResponse<String> handleUserLoop() throws Exception {
        List<String> error = new ArrayList<>();
        List<SlrUser> slrUsers = slrUserMapper.getListByParams(null);
        Map<Long, SlrUser> userMap = slrUsers.stream().collect(Collectors.toMap(SlrUser::getId, s -> s));
        for (SlrUser s : slrUsers) {
            if (isLoop(s, userMap)) {
                System.out.println("[" + s.getRealName() + "],id[" + s.getId() + "]");
                error.add(s.getId() + "");
            }
        }
        System.out.println("检查完毕");
        return new ListResponse<>(error);
    }

    /**
     * 快慢引用检测环<br/>
     * 快引用速度是慢引用的2倍
     *
     * @throws Exception
     */
    public boolean isLoop(SlrUser s, Map<Long, SlrUser> userMap) {
        SlrUser slow = s;
        SlrUser fast = s;
        while (slow != null && userMap.get(fast.getParentId()) != null) {
            if ((slow = userMap.get(slow.getParentId())) == null) {
                return false;
            }
            if ((fast = userMap.get(userMap.get(fast.getParentId()).getParentId())) == null) {
                return false;
            }

            if (slow == fast) {
                return true;
            }
        }
        return false;
    }

 

可以看出,检查出这四个用户是有环的,查询数据库,果不其然,到处是坑。反馈给运营同事后,及时调整代码和数据,系统回复正常! 

 所以说,迎着困难而上,依靠自己,我们都是巨人。

欢迎大家和帝都的雁积极互动,头脑交流会比个人埋头苦学更有效!共勉!

公众号:帝都的雁

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值