Java进阶之Dump文件初体验

视频地址:https://www.bilibili.com/video/BV1Ak4y137oh

学习文章:https://d9bp4nr5ye.feishu.cn/wiki/VQoAwlzrXiLFZekuLIyc1uK5nqc


最近线上频繁的内存告警,同事A通过分析dump文件解决了这个问题,我当然是不会放过这种学习的机会。


文章目录

    • 一、分析dump文件
      • 1、下载dump文件
      • 2、查看dump文件
      • 3、分析dump文件
    • 二、扩展:JVM内存结构,回收规则
    • 三、实战

一、分析dump文件


1、下载dump文件

步骤 命令 描述
找到Java服务 jps 找到需要观察的Java服务进程。 也可以使用 ps -ef grep java

1. jps 属于JDK提供的命令
2. ps 是Linux提供的命令
生成dump文件 jmap jmap -dump:file=/tmp/xdx_dump.bin 1

上面命令的含义就是在 /tmp 目录下生成一个名为 xdx_dump 的文件,格式为 bin,java服务的进程 1
压缩dump文件 tar 在真实线上生成的 dump 文件都很大,所以我们需要把它压缩成 tar.gz 文件,可以减少6倍以上的大小。

tar -zcvf xdx_dump_one.tar.gz xdx_dump_one.bin

上面命令的含义就是 把当前目录下的xdx_dump_one.bin文件,压缩成 xdx_dump_one.tar.gz 文件

2、查看dump文件


可以通过两种方式获取查看dump文件的软件

  1. 官网下载,可以拿到最新版本。 https://visualvm.github.io/
  2. 在JDK的 home/bin 目录下已经带了这个工具,并且还是中文的。

从服务器上下载压缩后的dump文件,拿到本地后再解压出来,双击打开 visualvm,把解压好的bin文件拖进去。然后你就可以看到堆中的数据信息了。

Java进阶之Dump文件初体验_第1张图片


3、分析dump文件


Java进阶之Dump文件初体验_第2张图片


毫无疑问,内存飙升,我们只需要看看当前系统中内存占比最大的是个什么东西,然后想办法干掉它。

Java进阶之Dump文件初体验_第3张图片


二、扩展:JVM内存结构,回收规则


为了方便理解后面实战的部分,需要准备一点前置知识:JVM内存结构和回收机制。

Hotspot VM将内存划分为不同的三个物理区:新生代、老年代、永久代。其中新生代又被分成Edun区和两个Survivor区。

Java进阶之Dump文件初体验_第4张图片

描述
新生代 大多数对象在这里被创建,其中很多对象的生命周期很短。每次新生代的垃圾回收(又称Minor GC)后只有少量对象存活,所以选用复制算法,只需要少量的复制成本就可以完成回收。

新生代内又分三个区:一个Eden区,两个Survivor区(一般而言),大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到两个Survivor区(中的一个)。当这个Survivor区满时,此区的存活且不满足“晋升”条件的对象将被复制到另外一个Survivor区。对象每经历一次Minor GC,年龄加1,达到“晋升年龄阈值”后,被放到老年代,这个过程也称为“晋升”。显然,“晋升年龄阈值”的大小直接影响着对象在新生代中的停留时间。
老年代 在新生代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代,该区域中对象存活率高。老年代的垃圾回收(又称Major GC)通常使用“标记-清理”或“标记-整理”算法。整堆包括新生代和老年代的垃圾回收称为Full GC(HotSpot VM里,除了CMS之外,其它能收集老年代的GC都会同时收集整个GC堆,包括新生代)。
永久代 要存放元数据,例如Class、Method的元信息,与垃圾回收要回收的Java对象关系不大。相对于新生代和年老代来说,该区域的划分对垃圾回收影响比较小。

Minor GC 的频率远高于Major GC,所以对象一旦到达老年代,它存活的时间会更长。


三、实战


在我们查看dump文件的时候,发现内存占用前几名(比例很大)都是日志输出的对象。伪代码如下:

List<XdxDTO> list = this.baseMapper.getAllList();
log.info("当前数据为:{}", JSONUtil.toJsonStr(list));

正常来说在方法里面返回的数据都属于局部变量,当方法执行完毕之后就没有引用会被回收。


但使用log输出的时候,它是异步输出的,消费能力不足的时候,就会把任务丢到阻塞队列里面去,如果某个大对象这时候在阻塞队列等待执行,恰好Edun区满了要被回收的时候,日志还没输出相当于还有引用这个数据,就不会被回收,如果系统持续压力过大,经过几轮Minor GC还没用回收,这个对象就会被放老年代,更难以回收了, 从而导致了内存OOM。

你可能感兴趣的:(#,Java,中阶,java,开发语言,dump文件,jmap,jvm)