Linux jstack 与 jmap 命令安装与实战
Linux jstack 与 jmap 命令安装与实战指南
本文是云悦智销项目后端运维技术栈的一部分。在部署 Java 项目后,jstack 和 jmap 是排查线上问题的必备工具。本文系统讲解如何在 Linux 服务器上安装这两个命令,以及基本的排查使用方法。
目录
- jstack 与 jmap 简介
- 安装准备
- 安装 JDK 开发版(获取 jstack 和 jmap)
- 验证安装
- jstack 命令实战
- jmap 命令实战
- 常见问题排查
- 项目中的应用场景
一、jstack 与 jmap 简介
1.1 两个命令的作用
┌──────────┬──────────────────────────────────────────────────────────┐ │ 命令 │ 作用 │ ├──────────┼──────────────────────────────────────────────────────────┤ │ jstack │ Java Stack Trace — 查看 Java 进程的线程堆栈信息 │ │ │ │ │ │ 应用场景: │ │ │ • 线程死锁(Deadlock) │ │ │ • 线程阻塞(Blocked) │ │ │ • 线程大量 WAITING(等待锁) │ │ │ • CPU 占用高的线程定位 │ │ │ • 线程池状态分析 │ │ │ │ │ │ 输出:线程状态、调用栈、等待的锁、持有的锁 │ ├──────────┼──────────────────────────────────────────────────────────┤ │ jmap │ Java Memory Map — 查看 Java 进程的堆内存分布 │ │ │ │ │ │ 应用场景: │ │ │ • 内存泄漏排查 │ │ │ • 对象数量统计 │ │ │ • 生成堆转储文件(heap dump)用于 MAT/VisualVM 分析 │ │ │ • 查看堆内存使用率 │ │ │ │ │ │ 输出:对象数量、内存分布、堆转储文件 │ └──────────┴──────────────────────────────────────────────────────────┘ 一句话: jstack → 看"线程"(CPU 高、卡顿、死锁) jmap → 看"内存"(内存溢出、内存泄漏)1.2 命令对比
┌──────────────┬─────────────────────────┬─────────────────────────┐ │ 维度 │ jstack │ jmap │ ├──────────────┼─────────────────────────┼─────────────────────────┤ │ 关注点 │ 线程状态 │ 内存分配 │ │ 触发场景 │ CPU100%、线程卡顿 │ OOM、内存持续增长 │ │ 输出文件 │ 文本(日志) │ 二进制(.hprof 文件) │ │ 影响性能 │ 几乎无影响 │ 生成 dump 时有短暂停顿 │ │ 常用参数 │-l-m-f│-heap-histo-dump│ └──────────────┴─────────────────────────┴─────────────────────────┘二、安装准备
2.1 为什么需要安装
问题: Linux 服务器上默认只安装了 JRE(Java Runtime Environment) JRE 不包含 jstack、jmap 等诊断工具 解决: 需要安装 JDK(Java Development Kit)的 devel 版本 开发版=运行时 + 诊断工具 + 开发工具2.2 检查系统环境
# 1. 查看 Linux 发行版cat/etc/os-release# 或lsb_release-a# 2. 查看当前 Java 版本java-version# 3. 查看已安装的 Java 包rpm-qa|grepjava# 4. 查看 jstack/jmap 是否已存在jstack--help# 如果提示 command not found,说明未安装jmap-h# 同上2.3 系统环境信息
┌────────────┬────────────────────────────────────────┐ │ 操作系统 │ CentOS7/8、RHEL、Ubuntu │ │ Java 版本 │ JDK1.8/11/17│ │ 包管理器 │ yum(CentOS/RHEL) │ │ │ apt-get(Ubuntu/Debian) │ └────────────┴────────────────────────────────────────┘三、安装 JDK 开发版(获取 jstack 和 jmap)
3.1 CentOS/RHEL 方式(yum)
# 步骤 1:切换到 root 用户suroot# 步骤 2:查看可用的 Java 版本yum list--showduplicate|grepjava# 或更精确的搜索yum list--showduplicate|grep-i"java.*jdk"# 步骤 3:安装对应版本的 JDK 开发版# 以 JDK 11 为例:yuminstalljava-11-openjdk-devel.x86_64-y# 如果是 JDK 1.8:yuminstalljava-1.8.0-openjdk-devel.x86_64-y# 如果是 JDK 17:yuminstalljava-17-openjdk-devel.x86_64-y3.2 Ubuntu/Debian 方式(apt-get)
# 步骤 1:更新包列表sudoapt-getupdate# 步骤 2:查看可用的 Java 版本apt-cachesearch openjdk# 步骤 3:安装 JDK 开发版# JDK 11:sudoapt-getinstallopenjdk-11-jdk-y# JDK 1.8:sudoapt-getinstallopenjdk-8-jdk-y# JDK 17:sudoapt-getinstallopenjdk-17-jdk-y3.3 安装命令流程图
安装流程: yum list--showduplicate→ 查看所有可用版本 │ ├──grep-i"java*"→ 过滤 Java 相关包 │ └── 找到对应版本 → 如 java-11-openjdk-devel.x86_64 │ yuminstalljava-11-openjdk-devel.x86_64-y│ └── 安装完成3.4 各版本对比
┌────────────────────────┬──────────┬──────────┬──────────┐ │ 包名 │ JDK 版本 │ 支持年份 │ 推荐度 │ ├────────────────────────┼──────────┼──────────┼──────────┤ │ java-1.8.0-openjdk │ JDK8│2014│ ⭐⭐⭐⭐ │ │ java-11-openjdk │ JDK11│2018│ ⭐⭐⭐⭐⭐ │ │ java-17-openjdk │ JDK17│2021│ ⭐⭐⭐⭐ │ └────────────────────────┴──────────┴──────────┴──────────┘ 云悦智销项目推荐使用 JDK11(企业级长期支持版本)四、验证安装
4.1 检查 jstack
# 方法 1:查看帮助信息jstack--help# 方法 2:查看版本jstack-version# 成功标志:显示帮助信息或版本号# 失败标志:command not found4.2 检查 jmap
# 方法 1:查看帮助信息jmap-h# 方法 2:查看版本jmap-version# 成功标志:显示帮助信息或版本号# 失败标志:command not found4.3 完整验证脚本
#!/bin/bash# verify_jdk.sh - 验证 JDK 安装echo"========== JDK 安装验证 =========="# 1. 检查 javaecho"1. Java 版本:"java-versionecho""# 2. 检查 javac(需要开发版才有的编译器)echo"2. javac 版本:"javac-versionecho""# 3. 检查 jstackecho"3. jstack 命令:"jstack--help2>&1|head-3||echo"jstack 未安装!"echo""# 4. 检查 jmapecho"4. jmap 命令:"jmap-h2>&1|head-3||echo"jmap 未安装!"echo""# 5. 检查 JAVA_HOMEecho"5. JAVA_HOME:"echo$JAVA_HOMEecho""echo"========== 验证完成 =========="五、jstack 命令实战
5.1 基本用法
# 语法:jstack[options]<pid># 常用选项:# -l 打印锁的额外信息(锁状态、等待队列)# -m 打印 Java 和本地(C/C++)帧的混合栈# -f 输出所有线程的栈追踪# -h 打印帮助信息5.2 实战 1:查看线程状态
# 步骤 1:查找 Java 进程 PIDjps-l# 列出所有 Java 进程ps-ef|grepjava# 或用 ps 命令# 步骤 2:使用 jstack 查看线程堆栈jstack<pid># 基本查看jstack-l<pid># 带锁信息查看jstack-m<pid># 含本地帧# 输出示例:"pool-1-thread-1"#15 prio=5 os_prio=0 tid=0x00007f...java.lang.Thread.State: RUNNABLE at com.example.OrderService.queryOrder(OrderService.java:50)at com.example.OrderController.list(OrderController.java:30)...5.3 实战 2:排查死锁
# 查看死锁(jstack 会自动检测)jstack-l<pid>|grep-A5"Deadlock"# 如果存在死锁,输出类似:"Thread-A"#10 daemon prio=5 os_prio=0 tid=0x00007f...java.lang.Thread.State: BLOCKED(on object monitor)at com.example.LockTest.methodA(LockTest.java:20)- waiting to lock<0x00000006c0e00d80>(a java.lang.Object)"Thread-B"#11 daemon prio=5 os_prio=0 tid=0x00007f...java.lang.Thread.State: BLOCKED(on object monitor)at com.example.LockTest.methodB(LockTest.java:35)- waiting to lock<0x00000006c0e00e00>(a java.lang.Object)"Found one Java-level deadlock:""=============================""Thread-A":waiting to lock monitor 0x00007f...(object 0x00000006c0e00e00)whichis held by"Thread-B""Thread-B":waiting to lock monitor 0x00007f...(object 0x00000006c0e00d80)whichis held by"Thread-A"5.4 实战 3:排查 CPU 高
# 步骤 1:找到 CPU 占用高的进程top-H-p<pid># 查看该进程下所有线程的 CPU 占用# 步骤 2:找到 CPU 最高的线程 PID(十进制)# 假设线程 ID 为 12345# 步骤 3:转换为十六进制printf"%x\n"12345# 输出:3039# 步骤 4:在 jstack 输出中搜索该十六进制线程 IDjstack<pid>|grep-A203039# 输出示例:"pool-1-thread-1"#15 prio=5 os_prio=0 tid=0x00007f...java.lang.Thread.State: RUNNABLE at com.example.OrderService.queryOrder(OrderService.java:50)at com.example.OrderController.list(OrderController.java:30)5.5 线程状态速查
┌────────────────┬────────────────────────────────────────────┐ │ 线程状态 │ 含义 │ ├────────────────┼────────────────────────────────────────────┤ │ RUNNABLE │ 正在运行或等待 CPU 调度 │ │ BLOCKED │ 等待获取监视器锁(synchronized 锁) │ │ WAITING │ 无限期等待其他线程执行(如 Object.wait()) │ │ TIMED_WAITING │ 有限期等待(如 Thread.sleep(time)) │ │ NEW │ 线程刚创建,尚未 start()│ │ TERMINATED │ 线程已执行完成 │ └────────────────┴────────────────────────────────────────────┘ 常见异常: • 大量 BLOCKED → 锁竞争严重,考虑优化锁粒度 • 大量 WAITING → 线程池可能等待任务,正常 • 大量 TIMED_WAITING → 线程在sleep或等待超时 • RUNNABLE 长时间不释放 → 可能有死循环或复杂计算5.6 jstack 常用命令汇总
# 查看指定进程的线程状态jstack12345# 查看线程堆栈并保存文件jstack12345>thread_dump.txt# 查看带锁信息的线程状态jstack-l12345# 持续监控(每 1 秒输出一次,共 10 次)foriin{1..10};dojstack12345sleep1done>thread_monitor.log# 查看 Java + 本地栈jstack-m12345六、jmap 命令实战
6.1 基本用法
# 语法:jmap[options]<pid># 常用选项:# -heap 打印堆内存配置和使用情况# -histo[:live] 打印堆中对象统计(:live 只统计存活对象)# -dump:format=b,file=<file> 生成堆转储文件# -finalizerinfo 打印等待回收的对象6.2 实战 1:查看堆内存使用
# 查看堆内存配置和使用情况jmap-heap<pid># 输出示例:Heap Configuration: MinHeapFreeRatio=40MaxHeapFreeRatio=70MaxHeapSize=2147483648(2048.0MB)NewSize=713031680(680.0MB)OldSize=1434451968(1368.0MB)Heap Usage: NEW generation(Eden +1Survivor): Eden Space: capacity=671088640(640.0MB)used=438536192(418.17MB)free=232552448(221.83MB)65.34905916213989% used OLD generation: capacity=1434451968(1368.0MB)used=1234567890(1177.26MB)free=199884078(190.74MB)86.06494242983903% used6.3 实战 2:查看对象统计
# 查看堆中各类对象的数量和大小jmap-histo<pid># 只看存活对象jmap-histo:live<pid># 输出示例:# Class Objects Size ByteCode1: byte[]1234569876549876542: java.lang.String56789123456712345673: com.example.Order2345623456782345678...6.4 实战 3:生成堆转储
# 生成堆转储文件(用于后续分析)jmap-dump:format=b,file=heapdump.hprof<pid># 只 dump 存活对象jmap -dump:live,format=b,file=heapdump.hprof<pid># 说明:# • heapdump.hprof 文件可能很大(几百 MB 到几个 GB)# • 生成时 JVM 会暂停几秒到几十秒(STW)# • 用 MAT (Memory Analyzer Tool) 或 VisualVM 打开分析6.5 内存问题分析流程
内存问题排查流程: JVM 内存持续增长 │ jmap-heap<pid>← 查看堆内存使用率 │ 如果 Old 区使用率>80% │ jmap-histo:live<pid>← 查看哪类对象最多 │ 如果发现某对象数量异常 │ jmap -dump:live,format=b,file=heapdump.hprof<pid>│ 下载 hprof 文件 → MAT / VisualVM 分析 │ 找到内存泄漏原因七、常见问题排查
7.1 jstack/jmap 命令找不到
问题: $ jstack bash: jstack:commandnot found 原因: • 只安装了 JRE(运行时环境),没有安装 JDK 开发版 •PATH环境变量未配置 解决:1. 安装 JDK 开发版: yuminstalljava-11-openjdk-devel.x86_64-y2. 配置 JAVA_HOME:exportJAVA_HOME=/usr/lib/jvm/java-11-openjdkexportPATH=$JAVA_HOME/bin:$PATH7.2 权限不足
问题: $ jstack<pid>Attaching to process ID<pid>... Error: Attaching to process<pid>failed 原因: • 当前用户没有权限查看该进程 • 进程属于其他用户(如 root) 解决: • 切换到进程所有者用户 • 或使用 sudo:sudojstack<pid>7.3 进程已终止
问题: $ jstack<pid>Attaching to process ID<pid>... No such process 原因: • 进程已退出或被杀死 解决: • 确认进程还在运行:jps 或ps-ef|grepjava• 重新启动 Java 应用 • 如果问题已发生,在启动时添加 JVM 参数以便诊断八、项目中的应用场景
8.1 云悦智销项目中的使用
在生产环境部署云悦智销项目后:1. CPU 占用100% →top-H-p<pid>找线程 → jstack 查看该线程堆栈 → 定位到具体代码行2. 内存持续增长,几天后 OOM → jmap-heap看 Old 区使用率 → jmap-histo:live看对象分布 → jmap-dump生成堆转储 → MAT 分析找到泄漏原因3. 用户反馈页面卡顿 → jstack 查看是否有大量线程 BLOCKED → 排查数据库锁或同步锁4. 线程池监控 → jstack 查看线程池线程状态 → 是否有大量 WAITING 或 BLOCKED8.2 推荐 JVM 启动参数
# 在启动 Java 应用时添加以下参数,便于线上排查:java-jarapp.jar\-XX:+HeapDumpOnOutOfMemoryError\# OOM 时自动 dump-XX:HeapDumpPath=/data/heap/\# dump 文件路径-Xms2g-Xmx2g\# 固定堆大小-Xmn512m\# 新生代大小-XX:+UseG1GC\# G1 垃圾回收器-XX:+PrintGCDetails\# 打印 GC 详情-XX:+PrintGCDateStamps\# GC 时间戳-Xloggc:/data/gc/gc.log\# GC 日志路径一句话总结:安装 jstack 和 jmap 的命令很简单(
yum install java-*-openjdk-devel),但真正重要的是掌握它们的排查方法。jstack 用来分析"线程问题"(CPU 高、死锁、卡顿),jmap 用来分析"内存问题"(OOM、内存泄漏)。
