程序员必备的十大技能(进阶版)之性能调优与故障排查(二)

简介: 教程来源 qfcrz.cn 本节系统梳理Java内存问题排查全流程:涵盖JVM内存结构(堆、元空间、直接内存等)、四大典型泄漏场景(静态集合、ThreadLocal、监听器、动态代理)、jstat/jmap/jcmd/Arthas等实战工具用法、MAT深度分析技巧(Dominator Tree、OQL查询),以及GC调优策略与I/O问题定位方法。

三、内存问题排查

3.1 内存问题分类
image.png
3.2 JVM内存结构回顾

┌─────────────────────────────────────────────────────────────┐
│                      JVM 运行时数据区                        │
├─────────────────────────────────────────────────────────────┤
│  ┌─────────────────────────────────────────────────────┐   │
│  │                       堆                             │   │
│  │  ┌───────────┐  ┌───────────┐  ┌───────────┐       │   │
│  │  │  新生代    │  │  老年代    │  │  元空间    │       │   │
│  │  │ Eden/S0/S1│  │   Old     │  │  Metaspace│       │   │
│  │  └───────────┘  └───────────┘  └───────────┘       │   │
│  └─────────────────────────────────────────────────────┘   │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │ 虚拟机栈     │  │ 本地方法栈  │  │ 程序计数器  │         │
│  │ (线程私有)  │  │ (线程私有) │  │ (线程私有) │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                    直接内存(DirectBuffer)            │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘

3.3 内存泄漏典型案例

// 案例1:静态集合持有对象引用
public class StaticListLeak {
    private static List<byte[]> list = new ArrayList<>();

    public void addData() {
        // 不断添加,永远不清理
        list.add(new byte[1024 * 1024]); // 1MB
    }
    // 解决方法:使用WeakHashMap或配置缓存淘汰策略
}

// 案例2:ThreadLocal未remove
public class ThreadLocalLeak {
    private static final ThreadLocal<byte[]> threadLocal = new ThreadLocal<>();

    public void process() {
        threadLocal.set(new byte[10 * 1024 * 1024]); // 10MB
        // 业务处理...
        // 忘记remove!
    }
    // 线程池中的线程复用,导致内存泄漏
    // 解决方法:finally块中调用threadLocal.remove()
}

// 案例3:监听器/回调未注销
public class ListenerLeak {
    private List<EventListener> listeners = new ArrayList<>();

    public void register(EventListener listener) {
        listeners.add(listener); // 注册后忘记注销
    }
    // 解决方法:使用WeakReference或提供unregister方法
}

// 案例4:动态代理导致的类加载器泄漏
// 解决方法:使用非持久化的类加载器或控制代理生成数量

3.4 内存问题排查工具实战

# 1. 查看堆内存使用情况
jstat -gc <PID> 1000 10

# 输出字段说明:
# S0C/S1C: Survivor0/1容量
# S0U/S1U: Survivor0/1使用量
# EC/EU: Eden容量/使用量
# OC/OU: 老年代容量/使用量
# MC/MU: 元空间容量/使用量
# YGC/YGCT: Young GC次数/总时间
# FGC/FGCT: Full GC次数/总时间
# GCT: GC总时间

# 2. 生成堆转储文件(dump)
jmap -dump:live,format=b,file=heap.hprof <PID>

# 3. 实时分析堆对象
jmap -histo:live <PID> | head -20

# 4. 使用jcmd(JDK 8+推荐)
jcmd <PID> GC.heap_info
jcmd <PID> GC.class_histogram
jcmd <PID> VM.native_memory

# 5. 使用Arthas分析
# 查看JVM内存分布
memory

# 查看堆对象统计
heapdump --live /tmp/heap.hprof

# 查看最占内存的对象
ognl '@com.alibaba.arthas.deps.org.apache.commons.lang3.ArrayUtils@toString(new java.util.ArrayList())'

3.5 堆内存分析(MAT/Eclipse MAT)

// MAT 使用技巧
// 1. 打开heap.hprof文件
// 2. 查看Leak Suspects Report(泄漏嫌疑报告)
// 3. 使用Histogram查看所有类的实例数
// 4. 使用Dominator Tree找出占用内存最大的对象
// 5. 使用OQL(对象查询语言)执行自定义查询

// OQL示例:查询所有byte数组
SELECT * FROM "[B"

// 查询特定类的实例
SELECT toString(s) FROM com.example.entity.Order s WHERE s.orderId.startsWith("TEST")

// 查看某个对象的GC Root路径
// 右键对象 → Merge Shortest Paths to GC Roots → exclude weak/soft references

3.6 GC调优实战

GC选择指南:
  内存 < 4GB: -XX:+UseSerialGC
  内存 4-8GB, 低延迟要求: -XX:+UseG1GC
  内存 > 8GB, 吞吐量优先: -XX:+UseParallelGC
  低延迟极端要求(<10ms): 使用ZGC或ShenandoahGC

G1GC调优参数:
  -XX:G1HeapRegionSize=16m              # Region大小
  -XX:MaxGCPauseMillis=200              # 目标暂停时间
  -XX:G1NewSizePercent=5                # 新生代初始比例
  -XX:G1HeapWastePercent=5              # 允许浪费比例
  -XX:ConcGCThreads=4                   # 并发GC线程数
  -XX:ParallelGCThreads=8               # 并行GC线程数
  -XX:InitiatingHeapOccupancyPercent=45 # MixedGC触发阈值

# 查看GC日志
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/path/to/gc.log

# GC日志分析工具
# GCeasy (https://gceasy.io)
# GCViewer

四、I/O问题排查

4.1 磁盘I/O问题

# 1. 查看磁盘I/O整体情况
iostat -x 1

# 关键指标:
# %util: 磁盘利用率(超过80%说明繁忙)
# await: 平均I/O等待时间(超过10ms说明慢)
# r/s, w/s: 每秒读写次数
# rkB/s, wkB/s: 每秒读写KB数
# avgqu-sz: 平均队列长度

# 2. 查看哪些进程在读写磁盘
iotop -o

# 3. 查看具体文件I/O(需要root)
lsof | grep deleted  # 查看被删除但仍被进程占用的文件

# 4. 慢SQL导致的磁盘I/O问题
# 查看MySQL慢查询日志
# 使用Percona Toolkit的pt-query-digest分析

4.2 文件描述符耗尽

# 查看进程打开的文件描述符数量
lsof -p <PID> | wc -l

# 查看进程限制
cat /proc/<PID>/limits

# 查看系统级别限制
ulimit -n
# 临时修改
ulimit -n 65535

# 永久修改 /etc/security/limits.conf
* soft nofile 65535
* hard nofile 65535

# 常见原因:
# 1. 连接池未关闭
# 2. 文件流未关闭
# 3. 临时文件未清理
// 文件流未关闭典型案例
public class FileLeak {
    public void readFile(String path) {
        try {
            InputStream is = new FileInputStream(path);
            // 业务处理
            // 忘记在finally中关闭
        } catch (IOException e) {
            // 异常处理
        }
    }

    // 正确写法
    public void readFileCorrect(String path) {
        try (InputStream is = new FileInputStream(path)) {
            // 业务处理
        } catch (IOException e) {
            // 异常处理
        }
    }
}

来源:
https://dffne.cn/

相关文章
|
1天前
|
设计模式 程序员
程序员必备的十大技能(进阶版)之设计模式与架构思维(二)
教程来源 https://vrhyh.cn/ 结构型模式关注类与对象的组合组织,提升系统灵活性与可维护性。含适配器(兼容接口)、装饰器(动态增强)、代理(控制访问)、外观(简化子系统)、组合(树形结构)五种核心模式,均通过封装、委托与抽象实现松耦合设计。
|
1天前
|
运维 Java 程序员
程序员必备的十大技能(进阶版)之性能调优与故障排查(一)
教程来源 https://qeext.cn/ 本文系统讲解性能调优与故障排查核心技能,涵盖故障方法论、CPU/内存/I/O/网络/数据库问题定位、Java诊断工具(Arthas/JVM)、全链路压测及混沌工程,辅以实战案例与黄金排查原则,助开发者从“重启党”进阶为问题终结者。
|
1天前
|
存储 Cloud Native 程序员
程序员必备的十大技能(进阶版)之云原生与容器化(一)
教程来源 https://oplhc.cn/ 云原生已成现代应用架构标准范式,本文系统解析容器原理、Docker实践、Kubernetes编排、服务网格、GitOps等核心技术,覆盖从虚拟化演进到可观测性建设的十大维度,助力开发者全面掌握云原生技术体系。
|
1天前
|
运维 Java 测试技术
程序员必备的十大技能(进阶版)之性能调优与故障排查(四)
教程来源 https://aescc.cn/ 本节系统介绍Java应用性能分析与稳定性保障核心实践:涵盖Arthas诊断、火焰图性能剖析、全链路压测方法论及JMeter实战、混沌工程故障注入(ChaosBlade),并复盘Full GC与数据库死锁两大典型故障案例,助力高效定位与解决线上性能问题。
|
1天前
|
设计模式 架构师 程序员
程序员必备的十大技能(进阶版)之设计模式与架构思维(五)
教程来源 https://xgmoi.cn/ DDD是应对复杂业务的架构方法论,含战略设计(限界上下文、上下文映射、子域划分)与战术设计(实体、值对象、聚合根、领域服务等),强调以领域模型驱动开发,实现业务与技术深度对齐。
|
1天前
|
运维 监控 程序员
程序员必备的十大技能(进阶版)之性能调优与故障排查(三)
教程来源 https://bgnno.cn/ 本节系统讲解网络与数据库性能问题排查:涵盖TCP连接状态分析、TIME_WAIT优化、延迟诊断、连接池配置;以及慢查询定位、索引失效识别、事务锁监控和连接池调优,助力高效定位并解决生产环境常见瓶颈。
|
存储 缓存 Oracle
常识四堆外内存
常识系列,作为一名互联网门外汉的科普系列 堆外内存除了在像netty开源框架中,在平常项目中使用的比较少,在现前的项目中,QPS要求高的系统中,堆外内存作为其中一级缓存是相当有成效的。所以来学习一下,文中主要涉及到这三分部内容 1. 堆外内存是什么?与堆内内存的区别 2. 怎么分配,与GC的影响 3. 开源框架使用 这篇文章写到最后,发现还只是回答了开源框架OHC的Why not use ByteBuffer.allocateDirect()?
1846 1
常识四堆外内存
|
8天前
|
存储 程序员 Linux
初级程序员必备的十大技能之 Git 版本控制(一)
教程来源 http://xcfsr.cn Git是程序员的“后悔药”与“时光机”:可随时回退错误修改、隔离并行开发、一键恢复稳定版本。作为分布式版本控制系统,它本地全量存储、离线可用、安全可靠,支撑全球90%以上团队高效协作。
|
18天前
|
前端开发 JavaScript 容器
前端组件库 ——LayUI 知识点大全(三)
教程来源 https://bncne.cn LayUI基础元素丰富实用:按钮支持多色、多尺寸及图标组合;图标为矢量字体,可自由缩放变色;表单模块集成验证与交互;layer弹层、table表格、laydate日期、upload上传等核心模块,让后台开发简洁高效。
|
18天前
|
前端开发 JavaScript 开发者
前端组件库 ——LayUI 知识点大全(四)
教程来源 https://zlpow.cn LayUI 2.8+/3.0 支持 CSS 变量主题定制、深浅色切换;提供移动端专用版本;支持按需引入与模块化加载;可开发自定义模块及集成 ECharts 等第三方插件,兼顾简洁性与扩展性,适合快速构建后台系统。

热门文章

最新文章