写点什么

Java 内存排查太难?阿里云操作系统控制台上线「内存诊断」新利器

  • 2025-11-24
    北京
  • 本文字数:3130 字

    阅读完需:约 10 分钟

大小:1.39M时长:08:04
Java内存排查太难?阿里云操作系统控制台上线「内存诊断」新利器

背景


随着汽车行业加速智能化转型,从传统线下 IDC 集群向云端迁移并进行容器化改造,经常会遇到关于 Pod 内存异常、Pod 发生 OOMKilled 的问题, 这些问题主要的矛盾点在于:

  1. Pod(容器)内存占用比 JVM 内存监控(堆内和堆外内存)占用大很多;

  2. 总是有一部分消失的内存无法找出具体是哪部分占用;

  3. 同一业务同一 JDK 版本,切换 OS 或容器化改造之后,才出现了 1、2 现象。


虽然 Java 工具千千万,但是选用什么工具排查起这类 Java 内存问题也是一个头疼的问题;甚至有时候翻遍了工具百宝箱,最后还是没有排查出问题的根因。经历过这些问题的洗礼之后,我们也从中总结了一些排查思路,并沉淀成一个阿里云操作系统控制台的 Java 内存诊断功能,帮助用户结合应用和操作系统的角度,快速揪出 Java 应用内存占用的元凶。

消失的 Java 内存

Java 内存组成


为了找出消失的内存,我们首先要了解 Java 进程的主要内存组成以及现有工具和监控主要覆盖的部分;如下图所示可分为:


JVM 内存


  • 堆内存:可通过 -Xms/-Xmx 参数控制,内存大小可通过 memorymxbean 等获取;

  • 堆外内存:包括元空间、压缩类空间、代码缓冲区、直接缓冲、线程栈等内存组成;它们分别可以通过-XX:MaxMetaspaceSize(元空间)、-XX:CompressedClassSpaceSize (压缩类空间)、 -XX:ReservedCodeCacheSize( 代码缓冲区)、-XX:MaxDirectMemorySize (直接缓冲)、-Xss(线程栈)参数限制。


非 JVM 内存


  • JNI 本地内存:即通过本地方法接口调用 C、C++ 代码(原生库),并在这部分代码中调用 C 库(malloc)或系统调用(brk、mmap)直接分配的内存。


常见 Java 内存黑洞


JNI 内存


通过对 Java 内存组成的了解,我们其实已经可以揭开第一个容易造成内存黑洞的隐藏 Boss-JNI 内存,因为这部分内存暂时没有工具可以获取其占用大小。


通常来说,编写相关业务代码的同学会认为代码中没有使用本地方法直接调用 C 库,所以不会存在这些问题,但是代码中引用的各种包却有可能会使用到 JNI 内存,比如说经典的使用 ZLIB 压缩库不当导致的 JNI 泄漏问题[2]


LIBC 内存


熟悉 Java 的同学都知道,JVM 是由 C++ 编写的,JVM 调用 malloc、free 申请/释放内存的过程中其实还要经过一个二道贩子 libc 库;以 gibc 中默认的内存分配器 ptmalloc 为例:


chunk 是 glibc 堆内存分配的最小单位,表示一段连续的内存区域。ptmalloc 会为每一个线程维护一个 Arena,每一个 Arena 中会有一个大 chunk(Top chunk)和 chunk 的缓存集合(bins);当应用通过 malloc、free 申请/释放内存时、ptmalloc 会优先从 bins 中拿取和释放 chunk,如果没有符合要求的 bins,再从 top chunk 里面获取、再没有就通过 brk、mmap 系统调用从 OS 获取。


从上面的流程,我们可以发现,libc 作为二道贩子,很有可能多申请、扣留一些内存,从而导致 JVM 内存和进程实际内存的差异。我们也总结一下 libc 常见的一些问题:

  • 多线程 64M Arena 内存占用,libc 会为每个线程创建一个 64M 大小的 Arena,默认配置下在线程数量较多时会导致一定的内存浪费 [3]

  • Top chunk 由于内存空洞导致无法及时释放回 OS [4]

  • bins 缓存,JVM 释放的内存被缓存在 bins 中,导致内存差异 [4]


透明大页


到了 OS 层,Linux 中的透明大页(Transparent Huge Page)机制也是造成 JVM 内存和实际内存差异的一大元凶。简单来说,THP 机制就是 OS 会将 4kb 页变成 2M 的大页,从而减少 TLB miss 和缺页中断,提升应用性能,但是也带来了一些内存浪费。如应用申请了一段 2M 的虚拟内存,但实际只用了里面的 4kb,但是由于 THP 机制,OS 已经分配了一个 2M 的页了[5]

通过阿里云操作系统控制台揪出 Java 内存占用元凶


操作系统控制台是阿里云推出的一站式运维管理平台,充分结合了阿里在百万服务器运维领域的丰富经验。集成了监控、系统诊断、持续追踪、AI 可观测、集群健康度和 OS Copilot 等核心功能,专门应对云端高负载、网络延迟抖动、内存泄漏、内存溢出(OOM)、宕机、I/O 流量分析及性能抖动等各种复杂问题。


下面将以汽车行业客户在从线下 idc 集群迁移至云上 ACK 集群时遇到的由于 JNI 内存泄漏导致 Pod 频繁 OOM 为例,介绍如何通过操作系统控制台的内存全景分析功能[5]来一步步找出 Java 内存占用的元凶。


背景:


客户云上多个集群多个服务中的一些 Java 业务 pod 在没有任何服务异常、请求异常、流量异常的迹象下会偶发 OOM,且从 jvm 监控上内存也没有很大的波动(客户设置 5G limit,正常水位在 3G 左右)。


排查过程:


尝试在内存高水位时对 Pod 发起内存全景分析,我们可以了解到当 Pod 中容器内存使用已经接近 limit,从诊断结论和容器内存占用分析中,我们可以看到容器内存主要是由于 Java 进程内存占用导致。



对 Java 进程发起内存分析,查看诊断报告。报告展示了 Java 进程所在 Pod 和容器的 rss 和 WorkingSet(工作集)内存信息、进程 Pid、JVM 内存使用量(即 JVM 视角的内存使用量)、Java 进程内存使用量(进程实际占用内存),进程匿名用量以及进程文件内存用量。



通过诊断结论和 Java 内存占用饼图我们可以发现,进程实际内存占用比 JVM 监控显示的内存占用大 570M,全都由 JNI 内存所贡献。



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



从内存分配火焰图中,我们可以看到主要的内存申请为 C2 compiler 正在进行代码 JIT 预热。但是由于诊断的过程中没有发现 pod 有内存突增,所以我们进一步借助可以常态化运行的 Java CPU 热点追踪功能[7]尝试抓取内存升高时的进程热点,并通过热点对比[8]尝试对内存正常时的热点进行对比。




通过热点栈和热点分析对比,发现内存突增时间点的 CPU 栈也是 c2 compiler 的 JIT 栈,且 c2 compiler 热点前有部分业务流量突增,且业务代码使用了大量反射操作(反射操作会导致 c2 compiler 进行新的预热)。


排查结论:


C2 compiler JIT 过程申请 JNI 内存,且由于 glibc 内存空洞等原因导致申请内存放大且延时释放。


缓解方法:


1.调整 C2 compiler 参数,让其编译策略更保守,可以尝试调整相关参数,观察内存消耗变化;

2.调整 glibc 环境变量 MALLOC_TRIM_THRESHOLD_,让 glibc 及时将内存释放回操作系统。


联系我们


您在使用操作系统控制台的过程中,有任何疑问和建议,可以扫描下方二维码或搜索群号:94405014449 加入钉钉群反馈,欢迎大家扫码加入交流。


操作系统控制台钉钉交流群


相关链接

【1】阿里云操作系统控制台 PC 端链接:

https://alinux.console.aliyun.com/

【2】java.util.zip 内存泄露:

https://bugs.openjdk.org/browse/JDK-8257032

【3】glibc 64M arena 内存浪费:

https://bugs.openjdk.org/browse/JDK-8193521

【4】glibc top chunk/fast bin 内存不回收:

https://wenfh2020.com/2021/04/08/glibc-memory-leak/#332-fast-bins-%E7%BC%93%E5%AD%98

【5】go 应用由于 thp 导致内存膨胀:

https://github.com/golang/go/issues/64332

【6】操作系统控制台内存全景分析:

https://help.aliyun.com/zh/alinux/user-guide/memory-panorama-analysis-function-instructions?spm=a2c4g.11186623.0.i1#undefined

【7】操作系统控制台热点追踪:

https://help.aliyun.com/zh/alinux/user-guide/process-hotspot-tracking?spm=a2c4g.11186623.help-menu-2632541.d_2_0_2_0.674a698dkLRagc&scm=20140722.H_2849500._.OR_help-T_cn~zh-V_1

【8】操作系统控制台热点对比分析:

https://help.aliyun.com/zh/alinux/user-guide/hot-spot-comparative-analysis?spm=a2c4g.11186623.help-menu-2632541.d_2_0_2_1.118569efrdHqOx&scm=20140722.H_2849536._.OR_help-T_cn~zh-V_1

2025-11-24 11:0713

评论

发布
暂无评论

【Flutter 专题】121 图解简易 Slider 滑动条

阿策小和尚

5月日更 Flutter 小菜 0 基础学习 Flutter Android 小菜鸟

深度分享|中小银行如何实现数字化转型,建设智能营销新体系?

索信达控股

大数据 数字化转型 金融 银行 营销数字化

麦肯锡最新报告 | 开发者速率成为企业增长助推剂

LigaAI

SaaS

Rust从0到1-泛型-定义

rust 泛型 generic

树莓派上的 K8S 集群挂了,怎么办?

百度开发者中心

百度 技术 经验分享

一种基于实时分位数计算的系统及方法

百度Geek说

云计算 大前端 云服务

ASP.NET Core整合Zipkin链路跟踪

yi念之间

强化学习落地:竞态场景下基于锁机制的闲置端口查用

行者AI

强化学习

云小课 | 华为云KYON之L2CG

华为云开发者联盟

虚拟私有云 华为云 大二层网络 KYON企业级云网络 L2CG

防火墙

escray

学习 极客时间 安全 5月日更 安全攻防技能30讲

前端开发:node.js的node包管理器npm安装以及使用

三掌柜

5月日更

Github Action 自动构建 Flutter Android Apk

Leetao

flutter Github Actions

【数据标注的类型有哪些】看懂这篇文章就够了!

澳鹏Appen

人工智能 机器学习 大数据 数据标注

云智慧发布《智能业务运维》2021年刊 邀您共览数字化运维全景象

云智慧AIOps社区

AIOPS 智能运维

架构是什么?空中楼阁?不切实际?

Java架构师迁哥

一文带你认识MindSpore新一代分子模拟库SPONGE

华为云开发者联盟

神经网络 mindspore 新一代分子模拟库 SPONGE 分子结构

☕【JVM 技术之旅】带你重塑对类加载机制的认识

码界西柚

JVM Java虚拟机 类加载器 原理分析 5月日更

毕业设计So Easy:基于Java语言西餐厅点餐系统

不脱发的程序猿

Java 开源 Java语言西餐厅点餐系统 毕业设计

网页端IM通信技术快速入门:短轮询、长轮询、SSE、WebSocket

JackJiang

websocket 消息推送 即时通讯 IM

为什么很多程序员,成为不了技术大牛?

实力程序员

Flink 实时计算在微博的应用

Apache Flink

flink

超详细的JQuery的 DOM操作,一篇就足够!

华为云开发者联盟

html 大前端 DOM 函数 JQuery框架

做开发,这几种锁机制你不得不了解一下

华为云开发者联盟

读写锁 自旋锁 互斥锁 优先锁

☕【JVM 技术之旅】深入挖掘Java对象的内存结构

码界西柚

JVM java对象分析 java对象 5月日更 内存对象结构

教你用 3 行代码发邮件

小匚

Python 学习 自动化

架构实战营模块4作业

阿体

k8s 上运行我们的 springboot 服务之——技术方案实现图

柠檬

k8s SpringBoot 2

Docker挂了,数据如何找回

运维研习社

Docker 运维 数据恢复 5月日更

微服务架构设计之解耦合

Damon

微服务 5月日更

解密华为云FusionInsight MRS新特性:一架构三湖

华为云开发者联盟

数据湖 云原生 华为云 FusionInsight MRS TechWave

dubbo的前世今生

捉虫大师

dubbo

Java内存排查太难?阿里云操作系统控制台上线「内存诊断」新利器_操作系统_肖杰韬_InfoQ精选文章