Java内存诊断

Java内存诊断功能适用于Java应用内存占用较高,但无法明确识别具体内存占用情况的场景。通过使用Java内存诊断功能,可以扫描当前Java进程及所在Pod的内存占用情况,从JVM视角和Java内存实际占用内存角度分别对Java进程内存占用进行拆解。本文介绍了Java内存诊断功能的使用说明。

适用范围

  • 地域

    本功能目前仅支持中国内地与中国香港。

  • 操作系统

    架构

    操作系统

    x86架构

    • Rocky Linux 9.5

    • Rocky Linux 9.1

    • Ubuntu 20.04

    • Alibaba Cloud Linux 3 容器优化版

    • Rocky Linux 8.8

    • Ubuntu 22.04

    • Alibaba Cloud Linux 3 Pro

    • Alibaba Cloud Linux 2/3

    • CentOS 7.6及更高版本,或CentOS 8

    • Anolis OS 7/8

    • Ubuntu 24.04

    ARM架构

    暂不支持。

准备工作

  • 若使用RAM用户,请确保阿里云账号(主账号)已将系统策略AliyunECSReadOnlyAccessAliyunSysomFullAccessRAM用户授权

  • 需要诊断的实例需要先通过操作系统控制台纳管,不支持非纳管诊断。

操作步骤

  1. 访问操作系统控制台-系统诊断,在页面左侧顶部,选择目标实例所在的地域

  2. 诊断模式设置为节点诊断,在诊断类型列表中,选择内存诊断。在诊断项列表中,选择Java内存诊断,并填写目标实例ID

  3. 选择Java内存诊断模式:

    1. 诊断Java进程:如果已知要诊断的Java进程的Pid,可以选择该模式,选择后需要输入要诊断的Java进程的Pid。

    2. 诊断Java进程所在Pod:如果Java进程运行在Pod中,可以选择该模式,选择后需要输入要诊断的Java进程所在的Pod.。

      当前仅支持单容器且Java进程作为容器1号进程的业务Pod。
  4. 选择JNI(Java Native Interface)内存分配profiling时长,单位为分钟,选择不开启则不进行profiling。

  5. 单击执行诊断后,可在诊断记录区域,单击查看报告

    image

诊断报告

  • 基础信息

    基础信息部分包含单次诊断的基本信息,包括诊断实例ID(资源ID)、诊断项、诊断报告ID及诊断发起时间。

  • 诊断结论

    若诊断到Java内存使用异常,将会把异常信息显示在本章节。

  • 诊断建议

    如果当前Java进程的内存使用异常,会给出进一步排查问题的建议。

诊断详情

  • Java进程信息概览 & Java进程所在Pod/容器信息

    展示了Java进程的基本信息,包括进程Pid、JVM内存使用量(即JVM视角的内存使用量)、Java进程内存使用量(可以理解为进程实际占用内存),进程匿名用量以及进程文件内存用量。image.png

    如果选用诊断Java进程所在Pod模式,还会展示pod已经容器的rssWorkingSet(工作集)内存占用。

    image.png

  • Java进程内存详情

    通过三个饼状图展示了Java堆内内存使用详情(JVM视角)、Java非堆内内存使用详情(JVM视角)和 Java进程实际内存使用详情;从而直观地看出在JVM视角下以及Java内存实际占用的内存中,哪部分内存占用较大。image.png

  • 堆内对象内存占用排序(Top10)

    列出当前JVM堆内占用内存排名前10的类的类名、类实例数和类实例占用的大小之和。

    image.png

  • 类加载器(classloader)加载数量排序(Top10)

    列出当前加载类数排名前十的类加载器的名称、加载类的数量以及加载器实例数量image.png

  • JNI(Java Native Interface)内存分配火焰图

    若开启了JNI(Java Native Interface)内存分配profiling,列出当前Java进程JNI内存分配调用火焰图,火焰图中为所有分配JNI内存的调用路径。(说明:由于是采样采集,火焰图中的内存大小不代表实际分配大小)。image.png

  • JNI(Java Native Interface)内存泄漏火焰图

    若开启了JNI(Java Native Interface)内存分配profiling,列出当前Java进程JNI内存泄漏调用火焰图,火焰图中为所有可能存在JNI内存泄漏的分配调用路径。(说明:由于是采样采集,火焰图中的内存大小不代表实际分配大小)。image.png

说明

只有发起诊断时选择开启JNI(Java Native Interface)内存分配profiling,等待对应时长后再打开诊断报告,火焰图数据才会展示。

实践案例

下面以案例来展示Java内存诊断如果解决云上较为常见的Java内存问题场景,如Pod OOM/内存占用高或Java进程内存占用高,但是JVM监控显示内存使用量不高。

问题现象

如下图示,容器监控系统发现其Kubernetes集群中某个Java业务PodRSS内存持续增加,增加到一定程度后发生OOM。但是通过JVM监控,堆内/堆外的内存使用量却比容器监控显示的RSS占用少了将近100MB,需要查明这差距的100MB内存占用的详细情况。

image.png

image.png

诊断分析

针对上述场景,使用Alibaba Cloud Linux控制台Java内存诊断相关业务Pod,诊断结果如下图所示:

image.png

从诊断结论和诊断建议中,可以得知存在JNI(Java Native)内存泄漏(由于JNI内存使用量通常不会被JVM统计,所以这也是RSSJVM内存大的主要原因之一)。

image.png

解决方案

再次发起诊断并开启JNI内存profiling。诊断完成后等待对应的profiling时长,然后再查看诊断报告。通过JNI内存分配和泄漏火焰图可以看到,由于代码中创建了大量java.util.zip.Deflater,但是却没有正确释放导致出现了JNI内存泄漏,通过将代码中的泄漏点进行优化,问题得到解决。

image.png