解决内存溢出
1. 修复问题思路
修复内存溢出问题的要具体问题具体分析,问题总共可以分成三类:
1.1 代码中的内存泄漏
代码中的内存泄漏在前面的篇章中已经介绍并提供了解决方案
1.2 并发引起内存溢出-参数不当
由于参数设置不当,比如堆内存设置 过小,导致并发量增加之后超过堆内存的上限。
解决方案:调整参数
1.3 并发引起内存溢出–设计不当
系统的方案设计不当,比如:
- 从数据库获取超大数据量的数据
- 线程池设计不当
- 生产者-消费者模型,消费者消费性能问题
解决方案:优化设计方案
2. 分页查询文章接口的内存溢出
小李负责的新闻资讯类项目采用了微服务架构,其中有一个文章微服务,这个微服务在业务高峰期出现了内存溢出的现象。
2.1 问题根源
文章微服务中的分页接口没有限制最大单次访问条数,并且单个文章对象占用的内存量较大,在业务高峰期并发量较大时这部分从数据库获取到内存之后会占用大量的内存空间。 
2.2 测试代码
java
// Controller 层
@GetMapping
public ResponseEntity<Page<TbArticle>> queryByPage(TbArticle tbArticle, int page, int size) {
return ResponseEntity.ok(this.articleservice.queryByPage(tbArticle, PageRequest.of(page, size)));
}
// Service 层
/**
* 分页查询
* @param tbArticle
* 筛选条件
* @param pageRequest 分页对象
* @return查询结果
**/
@Override
public Page<TbArticle> queryByPage(TbArticle tbArticle, PageRequest pageRequest) {
//long total = this.articleDao.count(tbArticle); // 总条数先不管减少干扰, 直接为0
return new PageImpl<>(this.articleDao.queryAllBylimit(tbArticle, pageRequest), pageRequest, 0);
}启动文章微服务:
sh
java -jar -Dserver.port=8081 -Darthas.http-port=3661 -Darthas.telnet-port=8565 -Xmx512m -Xms512m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/jvm/dump/jvm.hprof jvm-optimize-0.0.1-SNAPSHOT.jar访问文章微服务:
数据库数据,可以看到文章内容字段是很长内容:
如果持续不断有人访问这个接口,就会导致内存溢出。打开这个jvm.hprof,假设我们不知情,如何找到这个接口导致的内存溢出?
2.3 问题分析
使用MAT工具打开jvm.hprof文件,进行分析:
MAT提示我们有两个怀疑对象,第一个占用200.4MB,第二个占用153.2MB。MAT怀疑的对象1信息:
可以看出,第一个对象堆栈中使用多线程,端口是8081.
第二个对象是MySQL驱动的返回对象。大致能推测可能是大量的线程请求数据库,导致内存溢出。查看直方图和支配树,进一步分析:
由于是springboot项目,通过查看类HandlerMethod方法,
选中右键ListObjects, 选择Outgoing,查看里面包含的具体的Controller:
