Linux jstack 与 jmap 命令安装与实战指南

本文是云悦智销项目后端运维技术栈的一部分。在部署 Java 项目后,jstack 和 jmap 是排查线上问题的必备工具。本文系统讲解如何在 Linux 服务器上安装这两个命令,以及基本的排查使用方法。


目录

  1. jstack 与 jmap 简介
  2. 安装准备
  3. 安装 JDK 开发版(获取 jstack 和 jmap)
  4. 验证安装
  5. jstack 命令实战
  6. jmap 命令实战
  7. 常见问题排查
  8. 项目中的应用场景

一、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 82014     │ ⭐⭐⭐⭐    │
│ java-11-openjdk        │ JDK 112018     │ ⭐⭐⭐⭐⭐  │
│ java-17-openjdk        │ JDK 172021     │ ⭐⭐⭐⭐   │
└────────────────────────┴──────────┴──────────┴──────────┘

云悦智销项目推荐使用 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、内存泄漏)。

Logo

电影级数字人,免显卡端渲染SDK,十行代码即可调用,工业级demo免费开源下载!

更多推荐