文章目录
- 1、溢出场景
- 2、快照文件分析
- 3、本地环境复现
- 4、结论
- 5、解决思路
记录一个问题,工作中有个数据处理服务OOM,查了下镜像的dockerfile,发现JVM参数如下。很明显,一个数据服务,里面经手大量的数据对象,堆内存125的设置肯定不合理,调大至512m解决。
重装了Jmeter,遇到几个问题:
-
安装
-
打开文件报错
-
解析xml报错
继续看文章微服务的内存问题。
1、溢出场景
文章微服务,在业务高峰期发生内存溢出。
2、快照文件分析
分析生产环境内存溢出时的JVM快照文件(hprof文件),发现com.mysql.cj.jdbc.result.ResultSetImpl
是MAT的怀疑对象,而ResultSetImpl即MySQL查询返回结果的包装对象,因此想到可能是大量从数据库查询的结果保存在内存中,导致JVM内存溢出。
打开直方图和支配树,按照深堆倒叙排序,发现String对象、文章实体类对象TbArticle、ResultSetImpl对象排名靠前:
从当前线程入手,找到处理器方法HandlerMethod,List objects --> with outgoing references查看其关联的对象,在description中方找到当前线程在执行哪个方法
- with outgoing references:当前对象引用了哪些对象
- with incoming references:当前对象被哪些对象引用
找到了有问题的类和方法:
3、本地环境复现
造数据10w:
CREATE PROCEDURE generate_test_data()
BEGIN
DECLARE i INT DEFAULT 1;
WHILE i <= 100000 DO
INSERT INTO article (title,content) VALUES (CONCAT('string', i),SUBSTRING(MD5(RAND()), 1, 50));
SET i = i + 1;
END WHILE;
END;
CALL generate_test_data();
DROP PROCEDURE generate_test_data;
select COUNT(*) from article a ;
设置JVM参数:
-Xmx100m -Xms100m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/jvm/heapdemo.hprof
Jmeter模拟业务高峰:
内存溢出,分析快照文件:
4、结论
堆内存快照中的怀疑对象代码,此处只是一个简单的查询:service层调mapper,做一个简单的select *
@GetMapping
public ResponseEntity<Page<TbArticle>> queryByPage(TbArticle tbArticle, int page,int size) {
return ResponseEntity.ok(this.articleService.queryByPage(tbArticle, PageRequest.of(page,size)));
}
分页结果查询的条数太大,并且单条数据的对象也很大,如此,业务高峰期,JVM内存保存了大量的对象,导致内存溢出。
5、解决思路
- 限制单次最大访问条数
- 分页接口如果只是展示文章列表,就不要把最大的内容字段content也返回,以减少每个对象的大小
- 整合sentinel,高峰期限流
思路一最合理,修改代码,让最大条数为100:
/**
* 分页查询
*
* @param tbArticle 筛选条件
* @return 查询结果
*/
@GetMapping
public ResponseEntity<Page<TbArticle>> queryByPage(TbArticle tbArticle, int page, int size) {
size = Math.min(100, size);
return ResponseEntity.ok(this.articleService.queryByPage(tbArticle, PageRequest.of(page, size)));
}
本地启动Visual,Jmeter模拟并发,发现JVM内存在100M下也够用,图中每次的下调,代表的就是一次查询结束,不用的对象被回收:
Jmeter压测表现平稳: