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

相关推荐
往日情怀酿做酒 V17639296386 分钟前
Django基础配置
后端·python·django
2401_8576009518 分钟前
企业OA管理系统:Spring Boot技术实践与案例分析
java·spring boot·后端
running up that hill23 分钟前
数据库中的增删改查操作、聚合函数、内置函数、分组查询
java·数据库·sql·mysql
潜洋27 分钟前
Spring Boot 教程之六:Spring Boot - 架构
java·spring boot·后端·架构
希忘auto30 分钟前
详解RabbitMQ在Ubuntu上的安装
java·rabbitmq
铅华尽38 分钟前
Java---JDBC案例--手机信息管理系统
java·开发语言·智能手机
小码的头发丝、42 分钟前
Maven的安装与配置
java·数据库·maven
说书客啊1 小时前
计算机毕业设计 | SpringBoot+vue线上家具商城 家居商品购买系统(附源码+论文)
java·spring boot·node.js·vue·毕业设计·智能家居·课程设计
小阿龙...1 小时前
创建mapreduce项目使用maven
java·ide·hadoop·spark·big data
疯一样的码农1 小时前
使用命令行创建 Maven 项目
java·maven