JVM---常规堆内存溢出场景排查方法


前言

记录这篇文章的目的是在工作中遇到实际的内存溢出异常时,能根据异常的提示信息迅速得知是哪个区域的内存溢出,知道怎样的代码可能会导致这些区域内存溢出,以及出现这些异常后该如何处理。首先在这里是默认大家熟悉JVM内存模型和GC相关知识的。下面我们会简单模拟一下堆内存溢出的场景并演示如何使用工具进行分析。

一、内存溢出情景模拟

/**
 * @author haichi
 * @version 1.0
 */
public class HeapOOM {

    static class OOMObject {}

    public static void main(String[] args) throws InterruptedException {

        long maxMemory = Runtime.getRuntime().maxMemory();
        long totalMemory = Runtime.getRuntime().totalMemory();
        System.out.println(maxMemory);
        System.out.println(totalMemory);

        List<OOMObject> oomObjectList = new ArrayList<>();
        while (true){
            TimeUnit.MILLISECONDS.sleep(10);
            oomObjectList.add(new OOMObject());
        }
    }
}

1.启动参数设置自动导出

首先Java堆用于储存对象实例,我们只要不断地创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么随着对象数量的增加,总容量触及最大堆的容量限制后就会产生内存溢出异常。知道这点就好办了,我们只需要模拟出一个不断new对象且不会被GC的场景即可。满足这个条件的同时我们需要手动设置堆大小给定一个较小的值这样方面我们模拟与观察。首先我们在idea的JVM参数设置中给定一个合适的值,这里我给到了20m。同时 -XX:+HeapDump OnOutOfMemoryError -XX:HeapDump Path=F:\dump 前面参数是指定JVM在内存溢出的情况下自动导出出我们的内存快照,而后面的参数即是我指定了特定的本地路径。
在这里插入图片描述

2.java Visual VM

当然除了这种方式我们还可以有其它的选择,比如使用jdk自带的java Visual VM工具
在这里插入图片描述
接下来就会自动为我们导出快照到指定文件
在这里插入图片描述

3.运行期手动导出

jmap -dump:format=b,file=<dumpfile.hprof> <pid>

二、快照分析工具

1.Jvisualvm

直接双击打开jvisualvm.exe,点击文件->装入,在文件类型那一栏选择堆,选择要分析的dump文件,打开
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
结合

代码如下(示例):

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
import  ssl
ssl._create_default_https_context = ssl._create_unverified_context

2.MAT分析

下载地址:
http://www.eclipse.org/mat/downloads.php
相对前一个java自带的分析工具MAT更加智能直观一点,首先点击 Leak Suspects 来看一下

在这里插入图片描述

在这里插入图片描述
点击 Histogram 来看一下列出每个class产生了多少个实例,以及占有多大内存,所占百分比。明显可以看到光OOMObject对象就占用了90以上的堆内存溢出的问题也就大致锁定了方向
在这里插入图片描述
和Histogram类似,时间久了,通过多次对比也可以把溢出对象找出来,Dominator Tree和Histogram的区别是站的角度不一样,Histogram是站在类的角度上去看,Dominator Tree是站的对象实例的角度上看,Dominator Tree可以更方便的看出其引用关系。
在这里插入图片描述
通 过Histogram视图或者Dominator Tree视图,找到疑似溢出的对象或者类后,选择Path to GC Roots或者Merge Shortest Paths to GC Roots,这里有很多过滤选项,一般来讲可以选择exclude all plantom/weak/soft etc. references。这样就排除了虚引用、弱引用、以及软引用,剩下的就是强引用。从GC上说,除了强引用外,其他的引用在JVM需要的情况下是都可以 被GC掉的,如果一个对象始终无法被GC,就是因为强引用的存在,从而导致在GC的过程中一直得不到回收,因此就内存溢出了。
在这里插入图片描述

总结

jvm调优这方面经验还是很浅薄,工具使用的也不是很熟练。还需要继续沉淀加强自己的经验,后续会对这篇文章进行相应的追加和修改现在看起来还很粗糙。

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值