在线堆文件分析功能

一、项目背景

Java 服务发生 OOM(OutOfMemoryError)时,传统排查流程依赖人工操作:手动登录服务器、导出 .hprof 文件、本地安装 Eclipse MAT、加载分析,整个流程耗时长、依赖环境搭建,无法快速定位问题。

本项目实现了一套完全自动化的在线堆文件分析系统,支持一键触发、异步分析、结果回调,大幅缩短 OOM 排查链路。


二、系统架构

复制代码
调用方 (jMetis)
    │  POST /admin/heap/dumpAndAnalyze
    ▼
HprofController ──── enqueue ──▶ Redis SortedSet(任务队列)
                                        │
                        HeapAnalyzeQueueWorker(每3s轮询)
                                        │  setNX 双重锁
                                        ▼
                              HeapAnalyzeService.doAnalyze()
                            ┌──────────┼──────────┐
                         Captain    飞书云盘    携程Ceph
                            └──────────┼──────────┘
                                       │ NingParallelDownloader(分片并行)
                                       ▼
                              解压 (.gz / .zip / .tar.gz)
                                       │
                              MatHprofAIParser.parse()
                              (绕过MAT OSGi,纯JVM内分析)
                                       │ 7个分析维度
                                       ▼
                           结果写入Redis缓存(1天)
                                       │
                        HTTP Callback → jMetis 平台

三、核心亮点

3.1 三种堆文件下载方式,覆盖全场景

系统定义了统一的下载接口 IDownloadHeapDump,通过策略模式注入,支持三种来源:

下载方式 枚举值 说明
Captain 平台(自动触发) CAPTAIN (0) 调用 Captain API → 触发 jmap dump → 轮询任务状态 → 获取下载链接 → 下载
飞书云盘(用户上传后分析) FEI_SHU (1) 通过飞书开放平台 OAuth 获取 Token → 解析 fileToken → 并行下载
携程 Ceph 网盘 CEPH (2) 直接使用网盘下载链接并行下载

Captain 方式 为全自动模式:系统自动触发 dump(每日限制 3 次防止影响线上),轮询等待生成完成(最长 10 分钟),下载 URL 缓存 5 天避免重复触发。飞书和 Ceph 方式适合用户已手动导出堆文件后,提供链接进行在线分析。


3.2 突破 MAT OSGi 依赖,实现纯 JVM 内分析

Eclipse MAT 是业界标准的 Java 堆分析工具,但其设计依赖 Eclipse Platform 的 OSGi 容器(插件机制),无法直接嵌入 Spring Boot 服务内运行

本项目通过反射 + 动态代理技术,完整绕过了 OSGi 依赖链,核心手段如下:

绕过点 技术手段
IExtensionRegistry(OSGi 插件注册表) JDK 动态代理注入 Mock Registry,返回空扩展列表
PlatformActivator.context(BundleContext) 反射注入 Mock BundleContext,所有方法返回空/false
MATPlugin.tracker(插件追踪器) 反射注入 Noop IExtensionTracker
PreliminaryIndexImpl(包私有构造器) 反射访问包私有构造函数直接实例化
GarbageCleaner.clean()(包私有方法) 反射调用包私有静态方法

完整解析管道(HprofParseUtil.openSnapshotDirect):

复制代码
HprofIndexBuilder.fill()      // 解析 HPROF,写磁盘索引
→ GarbageCleaner.clean()      // 清理不可达对象
→ SnapshotImpl.create()       // 直接构建 ISnapshot

这样 MAT 的完整分析能力(对象直方图、GC Root 链、支配树等)可以在无 Eclipse 环境的生产服务器上直接使用。


3.3 分片并行下载,解决大文件传输瓶颈

堆文件通常较大(数 GB),NingParallelDownloader 实现了基于 HTTP Range 请求的分片并行下载:

  • HEAD 请求探测服务端是否支持 Range 及文件总大小
  • 最多 5 个并发分片,每片最小 50 MB,基于文件大小自动计算分片数
  • 使用 Ning AsyncHttpClient 实现非阻塞异步 IO,每个分片流式写盘,不占用 JVM 堆内存
  • 分片失败自动重试最多 3 次(基于 Guava Retryer)
  • 全部分片完成后使用 NIO FileChannel.transferTo 零拷贝合并,规避额外内存拷贝
  • 服务端不支持 Range 时自动降级为单线程流式下载

3.4 异步队列 + 双重锁,保障多实例安全

请求接入后立即返回 "已受理,分析完成后回调",真正的分析由后台 Worker 异步执行:

复制代码
Redis SortedSet(按入队时间排序)
        ↓ HeapAnalyzeQueueWorker 每3s轮询
   AtomicBoolean(单机锁)------ 防同机器并发
        ↓ claimed
   Redis setNX executingKey(多机锁)------ 防多实例竞争
        ↓ doAnalyze()
   finally: ZREM + DEL(executingKey) + 释放本地锁
  • 单机维度AtomicBoolean.compareAndSet 保证同一实例同一时刻只处理一个分析任务
  • 多机维度:Redis setNX 竞争锁,抢占失败的实例本轮跳过,任务保留队列等待下次重试
  • 故障恢复executingKey 设置 20 分钟 TTL,宕机后锁自动释放,任务可被其他实例接管

3.5 七维度全面分析,结果结构化输出

MatHprofAIParser 生成 MatHprofReportDraft,覆盖 7 个分析维度:

Section 内容
S1 类直方图(按实例数排序)
S2 类直方图(按内存大小排序 Top N)
S3 支配树 Top 对象(保留堆最大的对象)
S4 GC Root 引用链(泄漏路径追踪)
S5 线程快照全览
S6 按类分组的 Top 持有者
S7 大型集合对象(ArrayList / HashMap 等)检测

分析结果以结构化 JSON 缓存于 Redis(1 天有效期),相同 appId + podName 命中缓存直接返回,避免重复分析。


3.6 内存安全:防止 MAT 分析后内存泄漏

MAT 内部 BufferedRandomAccessInputStreamclose() 只关闭了文件句柄,未清空 pages 字段(1.5M+ SoftReference<Page>),导致整条 SnapshotImpl → IndexManager → pages 引用链长期残留在老年代。

本项目在分析完成后通过反射主动置空 indexManagerheapObjectReader 字段,切断引用链,再调用 System.gc() 触发回收,确保每次分析后内存得到及时释放。同时注册 JVM ShutdownHook,保证服务停机时临时文件也被完整清理。


四、与传统方案对比

维度 传统方式 本项目
触发方式 手动登录服务器执行 jmap 飞书机器人/API 一键触发
环境依赖 需要本地安装 Eclipse MAT 无需任何额外环境
分析速度 人工操作,至少 30 分钟起 全自动,分析完成后主动回调
下载渠道 只能 SCP/SFTP 拉取 支持 Captain/飞书云盘/携程网盘 3 种方式
多实例安全 无保障 双重锁保证串行安全
结果可复用 每次重新分析 Redis 缓存 1 天,相同文件秒返回
大文件传输 单线程,速度慢 分片并行,最高 5x 提速

五、总结

dumpAndAnalyze 功能将 Java OOM 排查从人工、离线、依赖本地工具 的传统模式,升级为自动化、在线、结构化输出的现代 AIOps 范式。核心技术突破点在于:通过反射+动态代理完整绕过 MAT 的 Eclipse OSGi 依赖,使业界标准的堆分析能力可以内嵌到任意 Spring Boot 服务中运行;同时配套多来源下载、分片并行传输、双重锁异步队列等工程化手段,保障了系统在生产环境下的稳定性与可扩展性。

相关推荐
青槿吖3 小时前
第二篇:从复制粘贴到自定义规则!Spring Cloud Gateway 断言 + 过滤全玩法,拿捏微服务流量管控
java·spring boot·后端·spring cloud·微服务·云原生·架构
SamDeepThinking3 小时前
C端多渠道用户体系设计:从需求到落地
java·后端·架构
天若有情6733 小时前
反向封神!C++ 全局单例不避反用,实现无锁多线程函数独占访问
java·javascript·c++
圆山猫3 小时前
[AI] [Linux] 教我编一个启用rust的riscv kernel用于qemu启动
linux·ai·rust
凤凰院凶涛QAQ3 小时前
《C++转JAVA快速入手系列》:基本通用语法篇
java·开发语言·c++
千寻girling3 小时前
机器学习 | 逻辑回归 | 尚硅谷学习
java·人工智能·python·学习·算法·机器学习·逻辑回归
Javatutouhouduan3 小时前
阿里2026最新Java面试核心讲(终极版)
java·java面试·java并发·后端开发·java程序员·java八股文·java性能优化
Elastic 中国社区官方博客3 小时前
Jina embeddings v3 现已在 Gemini Enterprise Agent Platform Model Garden 上可用
大数据·人工智能·elasticsearch·搜索引擎·ai·全文检索·jina