JVM常见问题排查方案

欢迎访问原文地址来阅读最新版本
转载请注明出处:https://kang.fun/dump
个人博客:kang.fun

一、主动分析

1. 分析CPU占用高的情况

  1. 查看CPU占用情况

    top

  2. 查看具体占用CPU的线程

    top -H -p <pid>

  3. 查询线程运行快照

    jstack <pid>

在该快照中可以搜索16进制的线程号,来查看运行代码位置

2. 查询内存占用情况

查询jvm中对象数量和大小

jmap -histo:live <pid>

3. 查看GC情况

通过jstat查看GC情况

jstat -gcutil <pid>

通过jmap查看jvm中堆内存占用详情

jmap -heap <pid>

二、错误日志分析

1. dump日志

如果打印dump日志方法

  • 在jvm启动的参数中,新增 jvm参数-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/admin/logs/java.hprof。这样在发生jvm 内存溢出时,就会直接dump出java.hprof 文件了

  • 使用jmap导出jvm内存信息

    jmap -dump:format=b,file=/home/admin/logs/heap.hprof  javapid
    
  • jstack可以查看当前java进程的dump信息

    jstack <pid>
    

dump日志分析

  • “http-nio-8081-exec-10” 线程名称
  • #25 线程编号
  • daemon 线程的类型
  • prio=5 线程的优先级别
  • os_prio=0 系统级别的线程优先级
  • tid=0x00007f87e028c000 线程ID
  • nid=0x6724 native线程的id
  • waiting on condition [0x00007f87b97d2000] 线程当前的状态

线程状态

状态说明
NEW未启动的。不会出现在Dump中。
RUNNABLE在虚拟机内执行的。运行中状态,可能里面还能看到locked字样,表明它获得了某把锁
BLOCKED受阻塞并等待监视器锁。被某个锁(synchronizers)給block住了
WATING无限期等待另一个线程执行特定操作。等待某个condition或monitor发生,一般停留在park(), wait(), sleep(),join() 等语句里
TIME_WATING有时限的等待另一个线程的特定操作。和WAITING的区别是wait() 等语句加上了时间限制 wait(timeout)
TERMINATED已退出的

方法调用修饰

  • locked: 成功获取锁
  • waiting to lock:还未获取到锁,在进入去等待;
  • waiting on:获取到锁之后,又释放锁,在等待区等待;
  • parking to wait for:等待许可证; (参考LockSupport.park和unpark操作)

2. core文件

当linux中进程崩溃时,linux会将程序当时的内存快照记录下来,保存到一个文件中,这个文件就是core文件。除了内存信息,还会记录寄存器信息(包括程序指针、栈指针等)、内存管理信息、其他处理器和操作系统状态和信息。

core文件存储位置

通过cat /proc/sys/kernel/core_pattern命令可以查看core文件保存的位置和文件名格式

JVMJava Virtual Machine)是 Java 程序运行的核心,掌握其工作原理和常见问题是成为高级 Java 开发者的关键。以下是 **JVM常见问题、性能瓶颈及使用技巧**,涵盖内存管理、GC 调优、OOM 排查、监控工具等内容。 --- ## ✅ 一、JVM 核心结构回顾 ```text +-----------------------------+ | Method Area | 类信息、常量、静态变量 +-----------------------------+ | Heap | 对象实例(新生代 + 老年代) +-----------------------------+ | Stack (Java) | 方法调用栈(每个线程独立) +-----------------------------+ | Program Counter | 当前线程执行的字节码位置 +-----------------------------+ | Native Method Stack | 调用本地方法(如 C/C++) +-----------------------------+ ``` ### 主要内存区域: | 区域 | 说明 | |------|------| | **堆(Heap)** | 最大区域,存放对象实例,GC 主战场 | | **方法区(Method Area)** | 存储类元数据、常量池、静态变量(JDK8 后为 Metaspace) | | **虚拟机栈(Stack)** | 每个线程私有,保存局部变量、方法调用栈帧 | | **程序计数器(PC Register)** | 记录当前线程执行到哪条字节码指令 | | **本地方法栈** | 支持 native 方法调用 | --- ## ✅ 二、JVM 常见问题及解决方案 ### 1. ❌ `java.lang.OutOfMemoryError: Java heap space` #### 原因: 堆内存不足,无法分配新对象。 #### 解决方法: ```bash # 增加堆内存大小 java -Xms512m -Xmx2g MyApp # 示例:设置初始 512MB,最大 2GB ``` > ⚠️ 注意:不要设置过大,避免长时间 GC 或系统资源耗尽。 #### 排查步骤: - 使用 `jmap` 导出堆转储文件: ```bash jmap -dump:format=b,file=heap.hprof <pid> ``` - 使用 **VisualVM** 或 **Eclipse MAT** 分析内存泄漏对象。 --- ### 2. ❌ `java.lang.OutOfMemoryError: Metaspace` #### 原因: 类加载过多(如动态生成类、反射频繁使用),Metaspace 空间不足。 #### 解决方法: ```bash # 设置 Metaspace 上限 java -XX:MaxMetaspaceSize=512m MyApp # 默认无上限,但建议限制防止过度占用 ``` #### 常见场景: - 使用 CGLIB 动态代理(Spring AOP) - OSGi 模块化框架 - 大量 JSP 页面编译(Tomcat) --- ### 3. ❌ `java.lang.StackOverflowError` #### 原因: 递归太深或无限循环导致栈溢出。 #### 示例代码: ```java public void recursive() { recursive(); // 无限递归 } ``` #### 解决方法: ```bash # 增加线程栈大小 java -Xss2m MyApp # 默认一般为 1M ``` > ⚠️ 不推荐盲目增大 `-Xss`,应优先修复逻辑错误。 --- ### 4. 🐢 频繁 Full GC 导致系统卡顿 #### 现象: 应用响应慢,日志中频繁出现 `Full GC`。 #### 查看 GC 日志: ```bash java -Xlog:gc*:gc.log -Xms512m -Xmx2g MyApp ``` 输出示例: ``` [GC (Allocation Failure) [PSYoungGen: 1024K->256K(2048K)] 1500K->700K(4096K), 0.002 secs] [Full GC (Ergonomics) [PSYoungGen: 256K->0K(2048K)] [ParOldGen: 444K->600K(3072K)] 700K->600K(5120K), 0.012 secs] ``` #### 解决方案: - 调整新生代与老年代比例: ```bash java -XX:NewRatio=2 -XX:SurvivorRatio=8 MyApp ``` - 使用更高效的垃圾回收器(见下文)。 - 减少大对象创建,避免过早进入老年代。 --- ### 5. 🔁 死锁(Deadlock) #### 现象: 多个线程互相等待对方释放锁,程序“卡住”。 #### 检测方式: ```bash jstack <pid> ``` 输出中会提示: ``` Found one Java-level deadlock: ============================= "Thread-1": waiting to lock monitor 0x00007f8b0c006e00 (object 0x00000007d5a3b9c8, a java.lang.Object), which is held by "Thread-0" "Thread-0": waiting to lock monitor 0x00007f8b0c004f00 (object 0x00000007d5a3b9d8, a java.lang.Object), which is held by "Thread-1" ``` #### 预防措施: - 锁顺序一致 - 使用 `tryLock(timeout)` 避免无限等待 - 使用工具类如 `ReentrantLock` 提供超时机制 --- ## ✅ 三、常用 JVM 参数配置 | 参数 | 作用 | |------|------| | `-Xms512m` | 初始堆大小 | | `-Xmx2g` | 最大堆大小 | | `-Xmn1g` | 新生代大小 | | `-Xss256k` | 线程栈大小 | | `-XX:MaxMetaspaceSize=512m` | Metaspace 上限 | | `-XX:+UseG1GC` | 使用 G1 垃圾回收器 | | `-XX:+PrintGCDetails` | 打印详细 GC 日志 | | `-XX:+HeapDumpOnOutOfMemoryError` | OOM 时自动生成堆 dump | | `-XX:HeapDumpPath=/logs/heap.hprof` | 指定 dump 文件路径 | --- ## ✅ 四、主流垃圾回收器对比(JDK 8 ~ 17) | GC 名称 | 特点 | 适用场景 | |--------|------|----------| | **Serial GC** | 单线程,简单高效 | 小型应用(Client 模式) | | **Parallel GC** | 多线程并行,吞吐量高 | 后台批处理任务 | | **CMS GC** | 并发标记清除,低延迟 | 老版本 Web 应用(已废弃) | | **G1 GC** | 分区收集,可控停顿时间 | 大内存服务(推荐 JDK 8~11) | | **ZGC** | <10ms 停顿,支持 TB 级堆 | 超大堆、低延迟要求(JDK 11+) | | **Shenandoah** | 类似 ZGC,Red Hat 开发 | OpenJDK 用户可选 | #### 推荐配置(生产环境): ```bash java \ -Xms4g -Xmx4g \ -XX:+UseG1GC \ -XX:MaxGCPauseMillis=200 \ -XX:+PrintGCDetails -Xlog:gc:/var/log/app/gc.log \ -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/app/heap.hprof \ com.example.MyApp ``` --- ## ✅ 五、JVM 监控与诊断工具 | 工具 | 用途 | |------|------| | **jps** | 查看 Java 进程 ID | | **jstat** | 实时查看 GC 统计 | | **jmap** | 生成堆转储文件 | | **jstack** | 查看线程栈(排查死锁) | | **jinfo** | 查看/修改 JVM 参数 | | **VisualVM** | 图形化监控(CPU、内存、线程) | | **Arthas(阿里开源)** | 生产环境在线诊断神器 | ### 示例:使用 `jstat` 监控 GC ```bash # 每秒输出一次 GC 情况,共 10 次 jstat -gc <pid> 1000 10 ``` 输出字段解释: | 字段 | 含义 | |------|------| | S0C/S1C | Survivor0/1 容量 | | EC | Eden 区容量 | | OC | Old 区容量 | | YGC | 年轻代 GC 次数 | | YGCT | 年轻代 GC 总耗时 | | FGC | Full GC 次数 | | FGCT | Full GC 总耗时 | | GCT | 所有 GC 总耗时 | --- ## ✅ 六、实战案例:如何定位内存泄漏? ### 步骤 1:确认是否存在内存增长趋势 ```bash jstat -gc <pid> 5s ``` 观察 `OGCMX` 和 `OU` 是否持续上升且不下降。 ### 步骤 2:导出堆 dump ```bash jmap -dump:format=b,file=heap.hprof <pid> ``` ### 步骤 3:使用 Eclipse MAT 分析 - 打开 `.hprof` 文件 - 查看 **Dominator Tree** - 找出占用最多内存的对象 - 查看 GC Roots 路径,判断是否该被回收 常见泄漏源: - 静态集合类持有对象引用(如 `static Map cache`) - 未关闭的数据库连接、流 - 监听器未注销 - 线程池未正确 shutdown --- ## ✅ 七、最佳实践建议 1. **合理设置堆大小**:根据业务负载测试确定 `-Xms` 和 `-Xmx`。 2. **开启 OOM 自动 dump**:便于事后分析。 3. **选择合适的 GC 策略**:Web 应用优先考虑 G1/ZGC。 4. **避免频繁创建大对象**:减少 Minor GC 压力。 5. **定期压测与调优**:上线前进行性能测试。 6. **使用 Arthas 在线诊断**:无需重启即可查看方法调用、线程状态等。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值