单个JVM堆内存减少400多MB,我们怎么做内存优化分析?

577 阅读4分钟

1.前言介绍

商品团队在SQL维度,做了些研究,发现了:无效更新、重复SQL、同模式SQL等方向,并做了些优化工作,提高了商品和库存系统的稳定性。2020年12月,小伙伴对商品系统的前台类目缓存数据进行分析,通过集中缓存一份数据,单个JVM内存就减少约400多MB内存,引发我们对JVM内存优化的思考与探索。

2.分析思路

获取到JVM 内存dump文件,结合Eclipse Memory Analyzer 工具分析统计内存中DO、VO、String等类生成的实例数据对象的数量以及对象唯一标识,如对象Id在内存中出现的次数、对出现频率高的对象查看被那些对象持有,并分析合理性,如下图所示:

image.png 其中,对象的唯一标识的举例:数据库表的行数据,映射为Java DO对象,数据库表的主键对应的DO对象的属性字段,可作为DO对象的唯一标志,以此类推根据具体情况确定。

3.分析实践

获取到系统的JVM内存Dump文件,打开后调整到柱状图一页,如下图: image.png

3.1 确定标识

可以发现:

1.“XXX.ctg.impl.CmCategoryCacheByVenderIdEnhancerBySpringCGLIBEnhancerBySpringCGLIB59273529” 和“XXX.ctg.impl.CmCategoryCacheByVenderId” 这几个类对象,虽然只有一个实例对象,由于引用了缓存对象,从这些实例开始的 Retained Heap最大。

2.“XXX.api.domain.ctg.CatSmallClassify” 类的实例对象个数多,Retained Heap大小也最大。分析代码,这个对象与数据库表对应,其Id字段可作为实例对象的唯一标识; 以下以该类作为分析对象展开。 image.png

导出 该类的内存实例列表,通过OQL 面板 ,操作如下: 在查询 框里输入以下语句: SELECT toString(s.id) FROM XXX.domain.ctg.CatSmallClassify s 执行,然后展开所有数据,然后导出这些Id列表到文件中;

image.png

3.2 重复分析

分析这些Id的重复度,统计后,一个ID最大的重复次数为2,如下:

image.png 重复百分比为63.9087%

image.png 即23万个DO中,有14万DO是重复出现的;

3.3 对象组织结构分析

取其中一个重复的Id 如 5256202, 在OQL控制面板输入框中,输入以下语句 SELECT * FROM XXX.domain.ctg.CatSmallClassify s WHERE ((toString(s.id) = "5256202"))

image.png 通过 path 2 GC Roots,排查这两个对象被谁持有。

image.png 如图,其中一个对象被: XXX.service.cat.impl.CatSmallClassifyServiceImpl类的 venderCodeCache 缓存容器持有

image.png 另外一个对象被:XXX.ctg.common.CatFrameworkConverter类的catSmallClassifyByVenderCache 缓存容器对象持有 image.png

3.4 缓存模型构建

其中缓存做法如下图: image.png CatFrameworkConverter中,维护的KV不一样,后面加了redis在JVM 和Mysql中做了一级缓存;

image.png CatSmallClassifyServiceImpl通过维护两个缓存对象,value都是单个CatSmallClassify对象,Key是不同的组织方式。

3.5 优化分析

通过以上guava容器对CatSmallClassify 对象实例的持有,以及其中的缓存对象实际是有表的全量数据,以及CatSmallClassifyServiceImpl.cache对象的定时刷新策略,是否可以重构缓存模型,依赖一份数据,再基于本地缓存计算其他的KV关系,如下:

image.png 其中底层数据更新,引起上层KV关系的重算,由于在JVM本地内部计算,减少了网络传输等,预计是可接受,后续根据业务等其他维度,做长远考量。 优化预期: image.png 可以发现 CatSmallClassify 实例对象占用的Retained Heap约为149MB,由上图知:

image.png 即 231053个Id 数字不一样列表,其中重复为2 的Id生成的DO对象有147663个,内存中该对象约为37.8万个。

image.png 如果去掉重复DO对象,预计每个JVM能节约: 147663/(231053+147663)*149MB= 58.0957MB的内存。

4 思考总结

以上通过内存文件,以及文件中对象的个数、重复度等,这些本身具有数量,对优化以及优化的预期效果能预知,认为是一个好的分析维度。又,其分析方法是通用的,也适用于其他项目。