一次线上系统,内存占用过多的分析过程

  • 背景介绍

        公司核心系统,由于时间比较久远,现在已经非常臃肿,系统为了对请求作出快速的响应,所以会加载一些数据到内存里,这就是问题所在,现在这些数据已经占到了4G多,但是领导要求不能做太大的改动,只能一点一点分析原因并解决。

  • 问题初步猜测

        一开始觉得内存占用比较多可以考虑将冷热数据分开,所以做了二级缓存,将冷数据放到了redis中,热数据使用guava cache,存在系统内存中,改造之后,内存占用变成了2G,但是还是感觉有点不对劲,因为这份元数据的真实大小其实只有500M,那剩下的内存都消耗在哪里呢?

        线上系统的几个主要的类加起来会有15个实例左右(系统用的Vertx,跟spring还是有点差别的,类的实例都是自己控制个数),每个实例,都会有一份元数据。

        本机测试1个实例:

        

        本机测试2个实例:

        

        本机测试7个实例:

        

        根据jvisualvm分析,元数据对象在内存中其实就只有一份,但是随着实例数的增加,String的数量再不断增加,根据代码,元数据的存储方式都是使用Map,Key基本上都是使用的是String,由此推断,线上内存大多数都被Map的Key占用了,由于本机数据较少,不太明显,所以打算使用线上数据分析。

  • 线上数据分析

        由于线上数据比较多,不能导出拿到本机上直接分析,所以考虑使用线上服务器分析完,本地查看分析结果。

  1.         线上使用jmap导出堆数据115433_XQj9_2361414.png                                 7个G,还真不小{哭笑}
  2. 下载MAT,并分析                                 
  3. MAT分析完,会有很多文件120045_H1wD_2361414.png

将这些文件打包回传到自己电脑,用Windows版的MAT直接打开hprof文件,这样就可以直接看结果了

  • MAT分析结果
  1.         总体占用                                    120234_J8B5_2361414.png
  2. 点击Leak Suspects  MAT会给出内存占用比较多的几个对象                  

        

查看具体占用

ConcurrentHashMap存储数据的单元其实就是Node,根据这张图就能很明显的看出,每个Node的占用空间最大的就是Key,进一步证实上面的推论

  • 分析代码发现,map的key都是用“+”拼接出来的

        121122_yRbC_2361414.png

       String的拼接其实等于new String,这样即使多个实例的Key的值是一样的,但是在内存中他们并不是同一个

121347_JLO8_2361414.png

  • 改进方案

        问题已经分析出来了,改进方案就是每个Key都调用一下String的intern方法

121513_xNJU_2361414.png

这个方法的大体意思就是,如果String 对象池中有这个值就直接返回引用,如果没有就添加到池中,并且返回

  • 改进结果

        修改前,两个主类实例的内存占用情况(线上数据)

 

修改后,两个主类实例的内存占用情况(线上数据)

 

通过修改前后两张图对比,在char数组和String占用上,大约节省了近300M

 

  • 后续补充

    String.intern()的使用,也要考虑到jdk版本,具体参看文章

http://tech.meituan.com/in_depth_understanding_string_intern.html

转载于:https://my.oschina.net/jiangzhixiong/blog/821259

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值