Java内存溢出OutOfMemoryError的产生与排查

在java的虚拟机异常中,有两个异常是你们比较关心的,一个是StackOverflowError,另外一个是OutOfMemoryError。今天咱们就来看看OutOfMemoryError是怎么产生的,以及如何去排查这个异常。java

概念

要了解什么是OutOfMemoryError,咱们能够直接看一下OutOfMemoryError的源码,在类上的英文注释很好的阐述了什么是OutOfMemoryError,翻译过来的意思是,因为内存不足,虚拟机没有可分配的内存了,垃圾回收器也不能释放更多的内存。在生产环境中,因为访问量过大,把内存吃满,会出现OutOfMemoryError的异常,小伙伴们若是没有经验的话,每每一筹莫展,究竟是真的内存不够用了,仍是本身的程序有问题,也不知道如何去排查这样的异常。git

模拟OutOfMemoryError

在这里,咱们写一段程序,来模拟一下OutOfMemoryError如何产生,咱们建立一个List对象,而后向里边不停的添加1M的Byte,以下;github

public static void main(String[] args) {
     List<Byte[]> list = new ArrayList<>();
     int i = 0;
     try {
         while (true) {
             list.add(new Byte[1024 * 1024]);
             i++;
         }
     } catch (Throwable e) {
         e.printStackTrace();
         System.out.println("执行了"+i+"次");
     }
 }
  • 咱们写了一个while(true)循环,每次都add一个1M的字节对象,1024*1024正好1M。
  • 咱们用i的值记录总共执行了几回。
  • 若是这样不停的执行下去,无论你有多大的内存,都会被吃光的。

咱们为了让程序运行时,快速的抛出OutOfMemoryError异常,能够在java的启动命令行增长启动参数,设置堆内存的初始值和最大值。这两个值在生产环境下,一般也是要配置的哦,要充分利用机器的内存嘛,若是不配置就会使用默认值。到时候因为内存不足向老板申请机器,可别挨骂哦~shell

那这两个参数怎么去加呢?工具

  • -Xms ,-Xms设置初始堆内存的大小
  • -Xmx, -Xmx设置最大堆内存的大小

一般状况下,这两个值设置成同样就能够了,总之,咱们设置了堆内存的大小。咱们在IDEA的启动配置中,统一设置堆内存为80M,以下;spa

好了~~咱们运行一下,看看会不会抛出OutOfMemoryError异常吧命令行

java.lang.OutOfMemoryError: Java heap space
	at com.diancan.JavaOOMDemo.main(JavaOOMDemo.java:14)
执行了14次

执行了14次,抛出了OutOfMemoryError异常。可是,若是抛出这样一个异常,咱们怎么去排查呢?就这一行日志也看不出什么来啊。翻译

排查

说到排查,若是咱们可以拿到异常时的内存快照,而后经过一些工具就能够了进行内存的分析了。那么咱们怎么去拿到内存溢出时的快照呢?其实,JDK也为咱们提供了这样的命令参数,咱们来看一下吧,日志

  • -XX:+HeapDumpOnOutOfMemoryError,从字面就能够很容易的理解,在发生OutOfMemoryError异常时,进行堆的Dump,这样就能够获取异常时的内存快照了。
  • -XX:HeapDumpPath=D:\heap-dump\ ,这个也很好理解,就是配置HeapDump的路径,方便咱们管理,这里咱们配置为D:\heap-dump,固然你也能够根据本身的须要,定义为其余的目录。

注意,HeapDumpPath的目录必定要手动建立好,若是没有这个目录,Dump会失败的。code

IDEA中的配置,如图:

咱们再运行一下程序,看看是什么样子,

java.lang.OutOfMemoryError: Java heap space
Dumping heap to D:\heap-dump\java_pid24312.hprof ...
Heap dump file created [123468648 bytes in 0.141 secs]
java.lang.OutOfMemoryError: Java heap space
	at com.diancan.JavaOOMDemo.main(JavaOOMDemo.java:14)
执行了14次

咱们发现日志上面多了点东西,建立了一个文件,在D:\heap-dump\java_pid24312.hprof。这个文件就是咱们的内存快照。那么问题来了,咱们如何查看这个文件呢?直接打开是不行的,用写字板等也是不行的,那怎么办?其实也没那么复杂,使用JDK自带的jvisualvm就能够查看。

这里边有个小坑,若是你们用JDK8,能够在JDK的bin目录下找到jvisualvm.exe,可是若是你使用的是JDK8以上的版本,就本示例中,使用的是JDK11,在bin目录下是找不到jvisualvm.exe的。你们能够去visualvm的主页下载。

咱们启动visualvm,进入到以下的页面,

而后,点击左上角的加载快照按钮,而后选择刚才咱们Dump的文件,

咱们重点看一下右侧中间的部分,

类的实例大小排序,能够看到,咱们的Byte占了96.5%。详细的信息,咱们能够点进去看,包括变量里存的内容,这样咱们就能够很快的定位到内存溢出的位置,而且能够判断是真的内存不够了,仍是咱们的代码出了问题。

相关文章
相关标签/搜索