大家好,今天要给大家介绍下OOM相关的知识点。
一般这种问题出现在生产环境,本地很少出现(除非你写了死循环),所以第一件要说的就是别慌,慌是没有用的,不多逼逼,开始正文
一、如何排查
1.指定启动参数,发生异常时打印dump文件
-XX:+HeapDumpOnOutOfMemoryError:表示当JVM发生OOM时,自动生成DUMP文件。
-XX:HeapDumpPath=${目录}:表示生成DUMP文件的路径,也可以指定文件名称,例如:-XX:HeapDumpPath=${目录}/java_heapdump.hprof。如果不指定文件名,默认为:java_pid[pid].hprof,默认在启动用户根目录。
项目启动一般会指定该参数,如果没有那就加上之后重启服务,复现OOM的问题,即可得到dump文件
2.下载该文件到本地
cd 到对应的目录
sz 命令下载文件到本地(可能因为该文件过大,下载缓慢,可以考虑别的方式下载,总之就是要把他下载到你本地)
3.Profiler分析该文件
idea高版本自带profiler工具,可直接打开.hprof文件
通过该工具,可以看到是哪个对象过大,代码里的拿一行导致的,一般看到这里就知道问题的所在了,接下来要做的就是看看怎么解决
二、问题的本质&解决办法
4.OOM的本质
我们知道jvm会对对象进行垃圾回收,从新生代到老年代,如果一个对象引用一直存在,则不会被回收,这种对象经过多次垃圾回收后最终会放在老年代,而jvm老年代的大小是固定的,如果一直有这种回收不掉的对象进来,就会发生OOM;另一方面jvm的堆大小是固定的,比如1G,你要强行放个2G的数据进来,那肯定是放不下,所以OOM
4.1 哪些情况会引起OOM
1.死循环创建对象,创建对象需要在堆中分配资源,死循环创建,堆迟早会被撑爆
2.加载进jvm的数据过大。比如一个sql查询没有加limit,而表本身有上亿条数据,一次全加载进内存中,自然要被撑爆;又比如加载外部的excel表格数据,表格中数据太多,也会撑爆内存
3.还有一种情况就是内存泄漏了,换句话说就是循环依赖。jvm判断一个对象是否存活(即是否需要回收该对象)有两种方法:引用计数法、可达性分析。两种方法的本质也就是确定一个对象是否还存在外部引用,如果存在,则不能回收。如果你的程序里正好有两个对象互相持有引用,那他们就都不能被回收,如果这样的对象随着时间会产生的越来越多,就会出现OOM,这也就是你们可能遇到的刚开始没事,运行了几个月之后出现了OOM
4.2 如何解决OOM
首先要明确的是具体问题具体对待了
1.假如你是mysql查询没有写limit,那加上之后就万事大吉了
2.假如你就是必须要加载一个大的excel,或者就是要在内存中操作大对象,
a)可以考虑增大jvm的大小
b)页面限制功能,比如导出一个大excel结果,在页面上限制该按钮的点击次数(点击后变灰)
3.如果是内存泄漏那就需要具体再去排查了