文章目录- 一、内存溢出的原因
- 二、模拟内存溢出
- 1、Main类
- 2、修改VM options参数
- 3、运行程序
- 4、用工具分析dump文件
- 5、解决思路
- 6、代码走查和分析
- 1、Main类
- 2、修改VM options参数
- 3、运行程序
- 4、用工具分析dump文件
- 5、解决思路
- 6、代码走查和分析
一、内存溢出的原因
内存溢出是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能提供的最大内存。 引起内存溢出的原因有很多种,常见的有以下几种:
- 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
- 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
- 代码中存在死循环或循环产生过多重复的对象实体;
- 使用的第三方软件中的BUG;
- 启动参数内存值设定的过小;
二、模拟内存溢出
1、Main类
代码语言:javascript复制public class Main {
public static void main(String[] args) {
List<Main> list = new ArrayList<>();
while (true) {
list.add(new Main());
}
}
}
2、修改VM options参数
代码语言:javascript复制-Xms20m -Xmx20m -XX: HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=C:UsersyanyunfanDesktop
我们通过-xms20m -Xmx20m两个参数,限制了Java堆的大小为20MB,不可扩展,后两个参数控制了当出现了OutOfMemoryError时,会Dump出当前内存的堆转储快照,并保存到指定位置中。
3、运行程序
4、用工具分析dump文件
去桌面找到dump文件,java_pid24096.hprof。 打开JDK自带的工具VisualVM,装入文件。
面板切换到"类"
这里可以很直观的看出,OutOfMemoryError产生的原因,是Main这个对象导致的。
5、解决思路
首先我们要排除内存泄露,即我们不需要的对象没有被回收掉。我们要找到泄漏的对象是如何与GC Root进行关联的?从而准确定位出泄漏代码的位置,然后进行修改。 如果不是内存泄漏,即堆中的对象必须存活,这个时候,我们可以通过调节虚拟机的堆参数(-Xms -Xmx),适当调大堆内存。但是在此之前,我们一定要检查一下代码是否存在优化的空间,如:是否存在某些对象的生命周期过长?是否可以使用享元模式减少对象数量?等等 内存溢出的解决方案: (1)修改JVM启动参数,直接增加内存。(-Xms,-Xmx参数一定不要忘记加。) (2)检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。 (3)对代码进行走查和分析,找出可能发生内存溢出的位置。 (4)使用内存查看工具动态查看内存使用情况
6、代码走查和分析
重点排查以下几点: (1)检查对数据库查询中,是否有一次获得全部数据的查询。线下测的没问题,一到线上数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。 (2)检查代码中是否有死循环或递归调用。 (3)检查是否有大循环重复产生新对象实体。 (4)检查List、MAP等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。