Win & Linux Java 项目堆栈信息调试教程

本文介绍如何利用堆栈信息快速定位多线程程序中的性能瓶颈,包括在Linux和Windows环境下查找高CPU占用线程的具体步骤,以及如何通过线程状态和堆栈信息进行代码优化。

使用堆栈信息排查系统性能问题,多线程程序调优(因为这类问题往往都不会输出日志或日志输出定位代码排查的范围太大),堆栈信息对这类问题定位速度快而精准。
查询堆栈信息全文涉及到的指令

线程状态相关 常见到的线程的状态(java.lang.Thread.State)

状态描述
RUNNABLE正在运行中的线程
LOCKED线程进行时受到阻塞,可能在等待释放锁,程序中可能有方法修饰了 synchronize (同步)
TIMED_WAITING延时线程,到了指定的时间的时候会被触发
WAITING线程未设置时间,只能通过interup或者notifyAll激活,通常用于不定时间不定的条件限制等待,需注意使用.wait()时需要在有同步锁的状态下否则会报错

指令相关

系统指令使用方法描述
Linuxjstackjstack <进程ID>输出Java进程里的线程堆栈信息
jstack <进程ID> | grep -A<筛选内容后输出几行的数字> <筛选内容>Java进程里的线程堆栈 信息筛选输出以及输出包括筛选信息后几行的内容
jstack <进程ID> -> <随意指定的文件名>输出Java进程里的线程堆栈 信息到文件中(若文件不存在则新生成文件并写入)
psps -T -p 查看进程下创建的所有线程列表信息
ps -aux|grep "筛选内容"筛选内容
ps -ef|grep "筛选内容"筛选内容
Windowsnetstatnetstat -ano|findstr "PID进程号"根据PID查询进程占用端口

Linux 系统下堆栈信息排查教程(以高CPU占用线程为例)

1.首先找到托管项目的Tomcat所在PID(进程ID)(ps -aux|grep “筛选内容” 或 ps -ef|grep “筛选内容”),如果项目占用内存或CPU过大可直接通过 top 指令查看并获取 PID
在这里插入图片描述
2.通过找到的PID来查看其进程下的所有线程运行情况(top -T -p )
PID=线程ID (该指令查出来的PID虽然是进程ID的名字但是实际上确实是线程ID)
TIME=线程执行时间 (可排查执行时间长的线程执行情况)
%CPU=占用CPU比例 (可排查高CPU占用的线程执行情况)
%MEM=占用内存比例 (可排查高内存占用的线程执行情况)
在这里插入图片描述
3.找到高 CPU 占用的线程,使用 printf “%x\n” <线程ID> 来得到该线程在堆栈信息中的十六进制数,然后使用 (jstack |grep -A20 0x<线程十六进制内容> --color) 指令查询并筛选线程堆栈信息 nid 为 tid 转成十六进制数之后的前缀加上0x的内容,并加上筛选内容后20行堆栈信息之后用 -color 给筛选内容染色输出显示
PS:每一个线程TID对应一个十六进制转换后前缀加上0x的NID
在这里插入图片描述
4.最后就能根据堆栈内容定位到问题代码所在地然后进行修改
在这里插入图片描述

Win 系统下堆栈信息排查教程(以高CPU占用线程为例)

Process Explorer 官方下载地址:https://docs.microsoft.com/zh-cn/sysinternals/downloads/process-explorer
Process Explorer 网盘下载地址:https://pan.baidu.com/s/1NxSvzqTL8yWKeiEqLeckGA

1.若找不到对应的是不是自己项目的进程则打开Windows的CMD控制台输入 netstat -ano|findstr “端口号” 来搜索端口对应的进程号PID,其他的端口号都为程序启动时涉及到使用的其他端口号(例如Redis[6379] MonogDb[27017] Mysql[6379]),无需理会,如果找到了Tomcat占用端口正好对应项目启动时设置的端口号,则证明该进程PID是我们要调试的堆栈信息的进程
在这里插入图片描述
2.根据Tomcat占用的端口找到项目的PID进程ID之后打开 Process Explorer 根据PID找到进程双击进入查看进程的线程信息,并根据高CPU占用率的线程信息拿到线程TID
在这里插入图片描述
3.根据找到的问题线程的线程ID TID 打开计算机切换到程序员模式点击十进制模式然后输入数字之后就能看到该数字转换成十六进制之后的值
在这里插入图片描述
4.用之前得到的进程ID 使用 jstack <进程ID> 指令输出进程的线程堆栈信息并且用计算后的十六进制的值前缀加上0x之后对线程堆栈信息进行筛选(由于是Windows系统我直接用的 Ctrl + F 筛选 0x3894)
在这里插入图片描述
5.最后就能根据堆栈内容定位到问题代码所在地然后进行修改
在这里插入图片描述

附加内容(其中3和4试过了,确实会如此)

释放锁情景

1.执行完同步代码块。

2.在执行同步代码块的过程中,遇到异常而导致线程终止。

3.在执行同步代码块的过程中,执行了锁所属对象的wait()方法,这个线程会释放锁,进入对象的等待池。

除了以上情况外,只要持有锁的线程还没有执行完同步代码块,就不会释放锁。因此在以下情况下,线程不会释放锁
不释放锁情景

4.在执行同步代码块的过程中,执行了Thread.sleep()方法,当前线程放弃CPU,开始睡眠,在睡眠中不会释放锁。

5.在执行同步代码块的过程中,执行了Thread.yield()方法,当前线程放弃CPU,但不会释放锁

6.在执行同步代码块的过程中,其他线程执行了当前对象的suspend()方法,当前线程被暂停,但不会释放锁。但Thread类的suspend()方法已经被废弃。

Demo下载: https://pan.baidu.com/s/1tEZyWppyA_dVpyBFA8i97w

&lt;think&gt;好的,我需要帮助用户找到在编程中获取当前线程堆栈信息的方法和示例。根据用户提供的引用资料,尤其是引用[1]和引用[3],看起来有不同的方法适用于不同语言和环境。首先,用户可能想知道Java中的方法,因为引用[1]提到了jstack工具,而引用[3]讨论了C语言的实现。另外,用户的问题没有指定编程语言,可能需要涵盖多种情况。 首先,针对Java,我知道jstack是一个命令行工具,可以打印Java进程的线程堆栈。用户可能需要代码层面的方法,比如在程序中获取堆栈跟踪。Java中可以通过Thread类的getStackTrace()方法,或者使用异常对象的printStackTrace()。例如,Thread.currentThread().getStackTrace()返回一个StackTraceElement数组,可以遍历输出。 然后,关于C/C++,引用[3]提到Windows上的StackWalker实现。可能需要使用Win32 API,比如CaptureStackBackTrace函数。此外,GCC编译器有backtrace函数,可以在Linux环境下使用。需要给出不同平台的示例。 还要考虑其他语言,比如Python,可以用traceback模块。但用户提供的引用中没有提到,可能需要补充,但根据用户的问题,可能更关注Java和C/C++,因为引用中提到了这些。 用户的问题还提到&ldquo;当前线程&rdquo;,所以需要区分获取当前线程还是所有线程的堆栈。例如,在Java中,jstack获取的是整个进程信息,而代码中的方法可能只获取当前线程的。 需要确保示例代码正确。例如,Java中使用getStackTrace()的代码示例,C/C++中使用backtrace的例子。同时,注意用户可能需要实际可运行的代码片段。 另外,引用[4]提到锁等待的情况,可能和线程堆栈分析有关,但用户的问题主要是获取堆栈信息的方法,所以可能不需要深入分析线程状态,但可以简要提及如何利用堆栈信息分析问题,比如检测死锁。 现在需要整理这些思路,分点回答,包括不同编程语言的方法,并提供示例代码。确保结构清晰,步骤明确,并引用提供的资料中的相关信息&lt;/think&gt;### 如何获取当前线程的堆栈信息? 在编程中获取线程堆栈信息调试和分析多线程问题的核心手段。以下是不同语言和场景下的实现方法及示例: --- #### 一、Java 中获取线程堆栈 1. **使用 `jstack` 命令行工具** 通过 `jstack` 直接输出进程内所有线程的堆栈信息[^1]: ```bash jstack &lt;pid&gt; &gt; stack_log.txt # 将堆栈信息输出到文件 ``` 2. **代码中动态获取当前线程堆栈** Java 的 `Thread` 类提供 `getStackTrace()` 方法: ```java public class StackTraceExample { public static void main(String[] args) { StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); for (StackTraceElement element : stackTrace) { System.out.println(element.toString()); } } } ``` 输出示例: ``` java.lang.Thread.getStackTrace StackTraceExample.main ``` --- #### 二、C/C++ 中获取堆栈信息Windows/Linux) 1. **Windows 平台** 使用 `CaptureStackBackTrace` API 或第三方库(如 [StackWalker](https://github.com/JochenKalmbach/StackWalker) 的 C 实现[^3]): ```c #include &lt;windows.h&gt; #include &lt;dbghelp.h&gt; void PrintStackTrace() { void* stack[100]; WORD frames = CaptureStackBackTrace(0, 100, stack, NULL); // 使用 SymFromAddr 解析符号信息(需配置 PDB 文件) } ``` 2. **Linux 平台(GCC)** 使用 `backtrace` 和 `backtrace_symbols` 函数: ```c #include &lt;execinfo.h&gt; #include &lt;stdio.h&gt; void PrintStackTrace() { void* buffer[100]; int size = backtrace(buffer, 100); char** symbols = backtrace_symbols(buffer, size); for (int i = 0; i &lt; size; i++) { printf(&quot;%s\n&quot;, symbols[i]); } free(symbols); } ``` --- #### 三、Python 中获取堆栈信息 使用 `traceback` 模块: ```python import traceback def print_stack(): for line in traceback.format_stack(): print(line.strip()) print_stack() ``` 输出示例: ``` File &quot;example.py&quot;, line 6, in &lt;module&gt; print_stack() ``` --- #### 四、应用场景 1. **调试死锁或锁等待问题** 通过堆栈信息可定位线程阻塞位置(如 `synchronized` 代码块或 `ReentrantLock` 等待)[^4]。 2. **性能分析** 高频调用的堆栈路径可帮助定位性能瓶颈[^2]。 ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值