Bohr-L Bohr-L
首页
技术
常见面试题
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

刘博

I'm a slow walker, But I never walk backwards.
首页
技术
常见面试题
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 数据处理与存储类

  • Spring 生态类

  • 缓存问题类

  • 多线程类

  • JVM 类

    • JVM 中一次完整的 GC 流程(从 ygc 到 fgc)是怎样的
    • 如何判断对象可以被回收
    • 你知道哪些垃圾收集算法
    • 虚拟机为什么使用元空间替换了永久代
    • 简述 CMS 垃圾收集器的工作流程,它有什么优缺点?
    • 简述一下 JVM 的内存模型
    • 说说堆和栈的区别
    • 简述 Java 的对象结构
    • JVM 中的即时编译器(JIT)如何工作?
    • 什么是内存屏障?JVM 如何使用内存屏障保证指令执行顺序?
    • 如何排查和解决 JVM 内存泄漏问题?有哪些常用的工具和方法?
      • JVM 如何处理异常?try-catch-finally 结构在字节码层面是如何实现的?
    • MySQL 类

    • Java 8 + 特性类

    • 其他技术类

    • 常见面试题
    • JVM 类
    刘博
    2025-12-28
    目录

    如何排查和解决 JVM 内存泄漏问题?有哪些常用的工具和方法?

    JVM 内存泄漏是指 “对象长期持有强引用,无法被 GC 回收,导致堆内存持续占用,最终引发 OOM” 的问题(如静态集合未清理、线程池未关闭、监听器未移除),排查和解决流程如下:

    # 1. 内存泄漏的排查流程

    # (1)确认内存泄漏现象

    通过监控工具观察 JVM 内存指标,判断是否存在内存泄漏:

    • 关键指标:
      • 堆内存使用率:长期持续上升(如每小时增长 10%),且 FGC 后无明显下降;
      • FGC 频率:频繁触发 FGC(如每分钟多次),且 FGC 后堆内存释放量极少;
      • OOM 日志:抛出OutOfMemoryError: Java heap space,且日志中显示 “老年代内存满”。
    # (2)获取堆转储文件(Heap Dump)

    堆转储文件记录了某一时刻堆内存中所有对象的信息(对象类型、数量、引用关系),是排查内存泄漏的核心数据:

    • 获取方式:
      1. JVM 参数配置:启动时添加-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/heapdump.hprof,当 OOM 发生时自动生成堆转储文件;
      2. jmap 工具:运行时手动生成,命令:jmap -dump:format=b,file=heapdump.hprof <pid>(<pid>为 Java 进程 ID,可通过jps命令获取);
      3. 可视化工具:通过 JVisualVM、Arthas 等工具一键生成堆转储文件。
    # (3)分析堆转储文件

    通过工具分析堆转储文件,定位内存泄漏的对象和引用链:

    • 核心分析点:
      1. 找出 “数量异常多” 的对象(如某类对象数量达 10 万级,远超正常业务需求);
      2. 查看异常对象的 “引用链”,找到持有该对象的强引用(如静态集合static List<User> list = new ArrayList<>());
      3. 结合业务代码,判断引用是否必要(如是否未清理过期数据、是否存在缓存未设置过期时间)。
    # (4)验证和修复问题
    • 根据分析结果,修改代码(如清理静态集合、关闭线程池、移除监听器);
    • 重新部署应用,通过监控工具观察堆内存使用率和 FGC 频率,确认内存泄漏是否解决。

    # 2. 常用工具

    工具名称 功能说明
    JVisualVM JDK 自带的可视化工具,支持监控堆内存、生成堆转储文件、分析对象引用链、查看 GC 日志
    MAT(Memory Analyzer Tool) 专业堆分析工具,支持自动检测内存泄漏(Leak Suspects)、分析引用链、计算对象占用内存
    Arthas 阿里开源的 Java 诊断工具,支持实时查看堆内存对象数量(heapdump命令)、跟踪方法调用(trace命令)
    jmap JDK 自带命令行工具,用于生成堆转储文件、查看堆内存对象统计信息(jmap -histo <pid>)
    jstat JDK 自带命令行工具,用于监控 GC 统计信息(如jstat -gc <pid> 1000,每 1 秒输出一次 GC 数据)
    GC 日志 通过-Xlog:gc*:file=gc.log:time,level,tags:filecount=5,filesize=100m配置,记录 GC 详细过程,分析 FGC 原因

    # 3. 常见内存泄漏场景及解决方案

    泄漏场景 原因分析 解决方案
    静态集合未清理 static List/Map存储大量对象,且未定期清理,对象长期持有强引用 1. 改用WeakHashMap(键为弱引用,对象无其他引用时自动回收);2. 定期清理集合(如list.clear())
    线程池未关闭 创建的线程池(如Executors.newFixedThreadPool(10))未调用shutdown(),线程长期存活,持有任务对象引用 1. 使用try-finally确保线程池关闭(finally { executor.shutdown() });2. 使用定时任务线程池(ScheduledExecutorService),任务完成后自动关闭
    监听器 / 回调未移除 注册的监听器(如 GUI 监听器、事件监听器)未移除,监听器持有对象引用 1. 在对象销毁时移除监听器(如eventBus.unregister(listener));2. 使用弱引用监听器(如WeakReference<Listener>)
    缓存未设置过期时间 Redis 缓存或本地缓存未设置过期时间,大量对象长期存储,无法回收 1. 为缓存设置合理的过期时间(如redis.expire(key, 3600));2. 采用 LRU 缓存策略(如Caffeine缓存),自动淘汰不常用对象
    数据库连接未关闭 Connection未调用close(),连接对象长期持有,且占用堆内存和数据库资源 1. 使用try-with-resources自动关闭连接(try (Connection conn = DriverManager.getConnection(...)) {});2. 使用连接池(如 HikariCP),自动管理连接生命周期

    上次更新: 12/30/2025
    什么是内存屏障?JVM 如何使用内存屏障保证指令执行顺序?
    JVM 如何处理异常?try-catch-finally 结构在字节码层面是如何实现的?

    ← 什么是内存屏障?JVM 如何使用内存屏障保证指令执行顺序? JVM 如何处理异常?try-catch-finally 结构在字节码层面是如何实现的?→

    最近更新
    01
    CPU 使用率较高排查和解决
    12-29
    02
    JVM OOM 问题如何排查和解决
    12-29
    03
    接口防刷怎么实现?
    12-29
    更多文章>
    Theme by Vdoing | Copyright © 2025-2026 Bohr-L's note
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式