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

相关推荐
niceffking几秒前
JVM HotSpot 虚拟机: 对象的创建, 内存布局和访问定位
java·jvm
菜鸟求带飞_4 分钟前
算法打卡:第十一章 图论part01
java·数据结构·算法
骆晨学长20 分钟前
基于springboot的智慧社区微信小程序
java·数据库·spring boot·后端·微信小程序·小程序
AskHarries25 分钟前
利用反射实现动态代理
java·后端·reflect
@月落26 分钟前
alibaba获得店铺的所有商品 API接口
java·大数据·数据库·人工智能·学习
liuyang-neu31 分钟前
力扣 42.接雨水
java·算法·leetcode
z千鑫35 分钟前
【人工智能】如何利用AI轻松将java,c++等代码转换为Python语言?程序员必读
java·c++·人工智能·gpt·agent·ai编程·ai工具
Flying_Fish_roe1 小时前
Spring Boot-Session管理问题
java·spring boot·后端
赚钱给孩子买茅台喝1 小时前
智能BI项目第四期
java·spring boot·spring cloud·aigc
hai405872 小时前
Spring Boot中的响应与分层解耦架构
spring boot·后端·架构