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用户,请确保阿里云账号(主账号)已将系统策略
AliyunECSReadOnlyAccess
和AliyunSysomFullAccess
为RAM用户授权。需要诊断的实例需要先通过操作系统控制台纳管,不支持非纳管诊断。
操作步骤
访问操作系统控制台-系统诊断,在页面左侧顶部,选择目标实例所在的地域
诊断模式设置为节点诊断,在诊断类型列表中,选择内存诊断。在诊断项列表中,选择Java内存诊断,并填写目标实例ID。
选择Java内存诊断模式:
诊断Java进程:如果已知要诊断的Java进程的Pid,可以选择该模式,选择后需要输入要诊断的Java进程的Pid。
诊断Java进程所在Pod:如果Java进程运行在Pod中,可以选择该模式,选择后需要输入要诊断的Java进程所在的Pod.。
当前仅支持单容器且Java进程作为容器1号进程的业务Pod。
选择JNI(Java Native Interface)内存分配profiling时长,单位为分钟,选择不开启则不进行profiling。
单击执行诊断后,可在诊断记录区域,单击查看报告。
诊断报告
基础信息
基础信息部分包含单次诊断的基本信息,包括诊断实例ID(资源ID)、诊断项、诊断报告ID及诊断发起时间。
诊断结论
若诊断到Java内存使用异常,将会把异常信息显示在本章节。
诊断建议
如果当前Java进程的内存使用异常,会给出进一步排查问题的建议。
诊断详情
Java进程信息概览 & Java进程所在Pod/容器信息
展示了Java进程的基本信息,包括进程Pid、JVM内存使用量(即JVM视角的内存使用量)、Java进程内存使用量(可以理解为进程实际占用内存),进程匿名用量以及进程文件内存用量。
如果选用诊断Java进程所在Pod模式,还会展示pod已经容器的rss和WorkingSet(工作集)内存占用。
Java进程内存详情
通过三个饼状图展示了Java堆内内存使用详情(JVM视角)、Java非堆内内存使用详情(JVM视角)和 Java进程实际内存使用详情;从而直观地看出在JVM视角下以及Java内存实际占用的内存中,哪部分内存占用较大。
堆内对象内存占用排序(Top10)
列出当前JVM堆内占用内存排名前10的类的类名、类实例数和类实例占用的大小之和。
类加载器(classloader)加载数量排序(Top10)
列出当前加载类数排名前十的类加载器的名称、加载类的数量以及加载器实例数量
JNI(Java Native Interface)内存分配火焰图
若开启了JNI(Java Native Interface)内存分配profiling,列出当前Java进程JNI内存分配调用火焰图,火焰图中为所有分配JNI内存的调用路径。(说明:由于是采样采集,火焰图中的内存大小不代表实际分配大小)。
JNI(Java Native Interface)内存泄漏火焰图
若开启了JNI(Java Native Interface)内存分配profiling,列出当前Java进程JNI内存泄漏调用火焰图,火焰图中为所有可能存在JNI内存泄漏的分配调用路径。(说明:由于是采样采集,火焰图中的内存大小不代表实际分配大小)。
只有发起诊断时选择开启JNI(Java Native Interface)内存分配profiling,等待对应时长后再打开诊断报告,火焰图数据才会展示。
实践案例
下面以案例来展示Java内存诊断如果解决云上较为常见的Java内存问题场景,如Pod OOM/内存占用高或Java进程内存占用高,但是JVM监控显示内存使用量不高。
问题现象
如下图示,容器监控系统发现其Kubernetes集群中某个Java业务Pod的RSS内存持续增加,增加到一定程度后发生OOM。但是通过JVM监控,堆内/堆外的内存使用量却比容器监控显示的RSS占用少了将近100MB,需要查明这差距的100MB内存占用的详细情况。
诊断分析
针对上述场景,使用Alibaba Cloud Linux控制台Java内存诊断相关业务Pod,诊断结果如下图所示:
从诊断结论和诊断建议中,可以得知存在JNI(Java Native)内存泄漏(由于JNI内存使用量通常不会被JVM统计,所以这也是RSS比JVM内存大的主要原因之一)。
解决方案
再次发起诊断并开启JNI内存profiling。诊断完成后等待对应的profiling时长,然后再查看诊断报告。通过JNI内存分配和泄漏火焰图可以看到,由于代码中创建了大量java.util.zip.Deflater,但是却没有正确释放导致出现了JNI内存泄漏,通过将代码中的泄漏点进行优化,问题得到解决。