【深入理解JVM】模拟内存溢出及排查分析

x33g5p2x  于2021-12-30 转载在 Java  
字(1.2k)|赞(0)|评价(0)|浏览(405)

一、内存溢出的原因

内存溢出是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能提供的最大内存。
   引起内存溢出的原因有很多种,常见的有以下几种:

  • 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
  • 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
  • 代码中存在死循环或循环产生过多重复的对象实体;
  • 使用的第三方软件中的BUG;
  • 启动参数内存值设定的过小;

二、模拟内存溢出

1、Main类

public class Main {
    public static void main(String[] args) {
        List<Main> list = new ArrayList<>();
        while (true) {
            list.add(new Main());
        }
    }
}

2、修改VM options参数

-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=C:\Users\yanyunfan\Desktop

我们通过-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回收。

相关文章