Linux jstack 与 jmap 命令安装与实战
·
Linux jstack 与 jmap 命令安装与实战指南
本文是云悦智销项目后端运维技术栈的一部分。在部署 Java 项目后,jstack 和 jmap 是排查线上问题的必备工具。本文系统讲解如何在 Linux 服务器上安装这两个命令,以及基本的排查使用方法。
目录
一、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 │
├──────────────┼─────────────────────────┼─────────────────────────┤
│ 关注点 │ 线程状态 │ 内存分配 │
│ 触发场景 │ CPU 100%、线程卡顿 │ 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 | grep java
# 4. 查看 jstack/jmap 是否已存在
jstack --help # 如果提示 command not found,说明未安装
jmap -h # 同上
2.3 系统环境信息
┌────────────┬────────────────────────────────────────┐
│ 操作系统 │ CentOS 7 / 8、RHEL、Ubuntu │
│ Java 版本 │ JDK 1.8 / 11 / 17 │
│ 包管理器 │ yum(CentOS/RHEL) │
│ │ apt-get(Ubuntu/Debian) │
└────────────┴────────────────────────────────────────┘
三、安装 JDK 开发版(获取 jstack 和 jmap)
3.1 CentOS/RHEL 方式(yum)
# 步骤 1:切换到 root 用户
su root
# 步骤 2:查看可用的 Java 版本
yum list --showduplicate | grep java
# 或更精确的搜索
yum list --showduplicate | grep -i "java.*jdk"
# 步骤 3:安装对应版本的 JDK 开发版
# 以 JDK 11 为例:
yum install java-11-openjdk-devel.x86_64 -y
# 如果是 JDK 1.8:
yum install java-1.8.0-openjdk-devel.x86_64 -y
# 如果是 JDK 17:
yum install java-17-openjdk-devel.x86_64 -y
3.2 Ubuntu/Debian 方式(apt-get)
# 步骤 1:更新包列表
sudo apt-get update
# 步骤 2:查看可用的 Java 版本
apt-cache search openjdk
# 步骤 3:安装 JDK 开发版
# JDK 11:
sudo apt-get install openjdk-11-jdk -y
# JDK 1.8:
sudo apt-get install openjdk-8-jdk -y
# JDK 17:
sudo apt-get install openjdk-17-jdk -y
3.3 安装命令流程图
安装流程:
yum list --showduplicate → 查看所有可用版本
│
├── grep -i "java*" → 过滤 Java 相关包
│
└── 找到对应版本 → 如 java-11-openjdk-devel.x86_64
│
yum install java-11-openjdk-devel.x86_64 -y
│
└── 安装完成
3.4 各版本对比
┌────────────────────────┬──────────┬──────────┬──────────┐
│ 包名 │ JDK 版本 │ 支持年份 │ 推荐度 │
├────────────────────────┼──────────┼──────────┼──────────┤
│ java-1.8.0-openjdk │ JDK 8 │ 2014 │ ⭐⭐⭐⭐ │
│ java-11-openjdk │ JDK 11 │ 2018 │ ⭐⭐⭐⭐⭐ │
│ java-17-openjdk │ JDK 17 │ 2021 │ ⭐⭐⭐⭐ │
└────────────────────────┴──────────┴──────────┴──────────┘
云悦智销项目推荐使用 JDK 11(企业级长期支持版本)
四、验证安装
4.1 检查 jstack
# 方法 1:查看帮助信息
jstack --help
# 方法 2:查看版本
jstack -version
# 成功标志:显示帮助信息或版本号
# 失败标志:command not found
4.2 检查 jmap
# 方法 1:查看帮助信息
jmap -h
# 方法 2:查看版本
jmap -version
# 成功标志:显示帮助信息或版本号
# 失败标志:command not found
4.3 完整验证脚本
#!/bin/bash
# verify_jdk.sh - 验证 JDK 安装
echo "========== JDK 安装验证 =========="
# 1. 检查 java
echo "1. Java 版本:"
java -version
echo ""
# 2. 检查 javac(需要开发版才有的编译器)
echo "2. javac 版本:"
javac -version
echo ""
# 3. 检查 jstack
echo "3. jstack 命令:"
jstack --help 2>&1 | head -3 || echo "jstack 未安装!"
echo ""
# 4. 检查 jmap
echo "4. jmap 命令:"
jmap -h 2>&1 | head -3 || echo "jmap 未安装!"
echo ""
# 5. 检查 JAVA_HOME
echo "5. JAVA_HOME:"
echo $JAVA_HOME
echo ""
echo "========== 验证完成 =========="
五、jstack 命令实战
5.1 基本用法
# 语法:
jstack [options] <pid>
# 常用选项:
# -l 打印锁的额外信息(锁状态、等待队列)
# -m 打印 Java 和本地(C/C++)帧的混合栈
# -f 输出所有线程的栈追踪
# -h 打印帮助信息
5.2 实战 1:查看线程状态
# 步骤 1:查找 Java 进程 PID
jps -l # 列出所有 Java 进程
ps -ef | grep java # 或用 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 -A 5 "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)
which is held by "Thread-B"
"Thread-B":
waiting to lock monitor 0x00007f... (object 0x00000006c0e00d80)
which is 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 输出中搜索该十六进制线程 ID
jstack <pid> | grep -A 20 3039
# 输出示例:
"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 常用命令汇总
# 查看指定进程的线程状态
jstack 12345
# 查看线程堆栈并保存文件
jstack 12345 > thread_dump.txt
# 查看带锁信息的线程状态
jstack -l 12345
# 持续监控(每 1 秒输出一次,共 10 次)
for i in {1..10}; do
jstack 12345
sleep 1
done > thread_monitor.log
# 查看 Java + 本地栈
jstack -m 12345
六、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 = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 2147483648 (2048.0MB)
NewSize = 713031680 (680.0MB)
OldSize = 1434451968 (1368.0MB)
Heap Usage:
NEW generation (Eden + 1 Survivor):
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% used
6.3 实战 2:查看对象统计
# 查看堆中各类对象的数量和大小
jmap -histo <pid>
# 只看存活对象
jmap -histo:live <pid>
# 输出示例:
# Class Objects Size ByteCode
1: byte[] 123456 987654 987654
2: java.lang.String 56789 1234567 1234567
3: com.example.Order 23456 2345678 2345678
...
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: command not found
原因:
• 只安装了 JRE(运行时环境),没有安装 JDK 开发版
• PATH 环境变量未配置
解决:
1. 安装 JDK 开发版:
yum install java-11-openjdk-devel.x86_64 -y
2. 配置 JAVA_HOME:
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk
export PATH=$JAVA_HOME/bin:$PATH
7.2 权限不足
问题:
$ jstack <pid>
Attaching to process ID <pid>...
Error: Attaching to process <pid> failed
原因:
• 当前用户没有权限查看该进程
• 进程属于其他用户(如 root)
解决:
• 切换到进程所有者用户
• 或使用 sudo:
sudo jstack <pid>
7.3 进程已终止
问题:
$ jstack <pid>
Attaching to process ID <pid>...
No such process
原因:
• 进程已退出或被杀死
解决:
• 确认进程还在运行:jps 或 ps -ef | grep java
• 重新启动 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 或 BLOCKED
8.2 推荐 JVM 启动参数
# 在启动 Java 应用时添加以下参数,便于线上排查:
java -jar app.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、内存泄漏)。
更多推荐




所有评论(0)