【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给到你的每一个细节,如果你对项目不熟悉,就算给到你了一个类路径,你也不一定能注意的到这是问题的产生点,这块是非常需要注意的。

相关推荐
XiaoLeisj2 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
paopaokaka_luck2 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
dayouziei2 小时前
java的类加载机制的学习
java·学习
码农小旋风3 小时前
详解K8S--声明式API
后端
Peter_chq3 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
Yaml44 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~4 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616884 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端
aloha_7894 小时前
从零记录搭建一个干净的mybatis环境
java·笔记·spring·spring cloud·maven·mybatis·springboot
记录成长java5 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet