【JVM内存医生🧑‍⚕️】内存分析,OOM分析,🧑‍⚕️:让我来帮你瞧瞧看是哪不对劲~

## JVM的内存分析神器

visualVM是甲骨文开发的一款JVM分析工具,可以分析JVM运行情况,GC情况,以及支持OOM分析,等强大功能。

官方网站:visualvm.github.io,下载地址:https://visualvm.g...

JVM内存溢出分析

内存dump文件分析

要获取OOM时的内存dump文件,需要在JVM启动参数上新增两个参数,一个是开启dump,另一个是文件保存位置。

dump文件后缀一般为.hprof,如果是这种文件的话一般来说就是JVM内存dump文件了。

第一步,将文件复制到本地

从服务器下载.horof文件到本地,下载方法不在赘述,k8sdocker、原生linux部署,下载方式可能会有差异,具体请Google

下载完成后你就会得到一个体积挺大的文件,我这个文件是直接使用visualVM进行dump的,放心,和OOM自动dump的是一样的。

第二步,使用visualVM打开文件

打开visualVM,点击File再点击Load

等待加载完成后会打开一个默认的界面:Summary界面

这个界面包含了JVM的一些列信息:

  • 堆内存信息:堆大小、加载的类、类实例、GC Roots、等..
  • JVM环境信息:系统版本、OS平台、JavaHome地址、Java版本、虚拟机厂商等...
  • JVM启动参数,需要点击:System Properties [show]才可以展示
  • 类实例的具体个数、类实例对应的大小
  • 等...

我们先看Class by Number Of Instances,这块展示的类实例的具体信息,我们可以得到以下结果:

  • Object类在堆中共存在,157705180,1.5亿个,占比100%
  • char[]数组在堆中共存在:9312个,占比0%

这不用往下看了,Object类实例明显不正常,什么代码会创建这么多Object。

我们直接双击Object,打开具体有哪些Object实例。

在这个界面我们可以看到所有的ObjectInstance,因为太多展示不完,所以最后一行就会显示:另外还有157705080个instances,点击就能查看更多

我们的目的就是要找到这些对象不能被回收的原因是什么,我们都知道在JVM里,如果一个对象的GC Root还在,那它就不能被回收,所以我们点击工具栏的GC Root按钮,查看这些对象的GC Root是谁

我们可以随便多点几个,结果如表格所示

对象实例 GC ROOT 图片
java.lang.Object#5 elementData in java.util.ArrayList#1 GC root - Java frame : 157,704,907 elements
java.lang.Object#6 elementData in java.util.ArrayList#1 GC root - Java frame : 157,704,907 elements
java.lang.Object#7 elementData in java.util.ArrayList#1 GC root - Java frame : 157,704,907 elements
java.lang.Object#8 elementData in java.util.ArrayList#1 GC root - Java frame : 157,704,907 elements

点击图片,或右击->在新页标签内查看,即可查看大图

可以了,以及不要点了,从GC ROOT给到的数据中可以看到,Object对象的引用根,在一个名为:elementData的对象里,elementData属于ArrayList,这个elementData中存在1.5+亿个Object,出问题的地方就在这了。

我们再次双击GC Root选卡卡中的elementData节点

点击<fields><refernces>左侧的加号即可查看详细信息。

我们可以从references中看到,这个ArrayList是个static字段,名叫OOM_LIST,处于cn.yufire.learing.OOMSample类中。

<fields>中可以看到类的具体信息,我们的类是ArrayList所以就是ArrayList内部的字段信息,可以看到内部已经操作157704908次(modCount=157704908)

我们找到对应位置的代码查看,究竟做了什么:

java 复制代码
public class OOMSample {
    private static List<Object> OOM_LIST = new ArrayList<>();

    public static void main(String[] args) {
        ThreadUtil.sleep(3000);

        new Thread(new AddObjectToMemory()).start();
        new Thread(new AddObjectToMemory()).start();
        new Thread(new AddObjectToMemory()).start();
        new Thread(new AddObjectToMemory()).start();
        new Thread(new AddObjectToMemory()).start();

        ThreadUtil.sleep(22222222222L);

    }


    public static class AddObjectToMemory implements Runnable {
        @Override
        public void run() {
            while (true) {
                synchronized (OOMSample.class) {
                    OOM_LIST.add(new Object());
                }
            }
        }
    }
}

原来是一个死循环在疯狂执行new Object()OOM_LIST.add()这两个操作,怪不得堆中会有1.5亿+个对象,并且不能被回收,因为OOM_LIST是一个static对象,本身就不会被回收,并且也没有地方将其设置为null,就会导致GC回收不掉了。

剩下的就是修改代码逻辑,优化对象的创建了。

我这个.hprof文件是自己dump下来的,真实生产环境会比这个复杂的多,对象也会比较复杂,如果你对项目特别熟悉的话,就不会错过visualVM给到你的每一个细节,如果你对项目不熟悉,就算给到你了一个类路径,你也不一定能注意的到这是问题的产生点,这块是非常需要注意的。

相关推荐
To_OC1 小时前
从一行入口代码啃透 NestJS:工厂模式、装饰器和依赖注入是怎么串起来的
后端·设计模式·nestjs
Python私教1 小时前
多个项目怎么安全合并?先适配,再切换
后端·python·架构
2601_963870202 小时前
【计算机毕业设计】基于Spring Boot的专科医院医疗管理系统
java·spring boot·课程设计
Bingo_BIG2 小时前
Java Spring 批量修改,实体、接口、方法的定义
java·spring
画中有画2 小时前
软件架构中质量属性(性能、安全、可扩展性)的权衡设计
java·运维·安全
Shaoxi Zhang2 小时前
JAVA学习笔记035——对象和JSON格式
java·笔记·学习
赵丙双2 小时前
CountDownLatch 源码分析
java·aqs·countdownlatch
Python私教3 小时前
从表格到管理系统:别先写页面,先补齐权限、流程和审计
数据库·后端·架构
余额瞒着我当琳3 小时前
C++--深拷贝三件套 + swap + 写时拷贝 + vector 扩容 + reserve
java·开发语言·c++
thefool1122663 小时前
翻转二叉树
java