使用jstack命令的一次排坑记录

在使用JedisCluster实现分布式锁时,高并发导致系统假死。通过jstack发现大量JedisPool线程处于WAITING状态,调整MaxWaitMillis参数至1000后,问题得以解决。

昨天使用Redis实现了一个分布式锁,在对这个锁进行压测的过程中,系统出现了假死状态。

我用的是JedisCluster,当并发达到一定数目之后,redis命令就不再执行了,tomcat也无法再对外提供任何服务。后台不报任何异常,CPU占用率并不高,内存也没有爆表。种种迹象都明,可能是系统中的某些线程发生了阻塞,也许是死锁,也许是其它。

开始我以为是我写的分布式锁有问题,于是以各种单线程或低并发做测试,然而并没有发现什么问题;一次只lock一个资源,也不可能导致死锁。所以应该就是高并发导致了系统产生某种故障。

于是我想到了用jstack来查看jvm中线程的运行情况。

当系统再次发生阻塞时,首先在命令行中敲入:jps,拿到当前tomcat的pid 12076,之后在命令行中敲入jstack -l 12076 >> E:/jstack_info1.txt,将thread dump记录输出到一个文件中。

打开后一看,发现有大量的JedisPool线程处于WAITING状态:

“http-nio-8092-exec-200” #241 daemon prio=5 os_prio=0 tid=0x0000000024b2d000 nid=0x6d98 waiting on condition [0x000000003821c000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x0000000779897f18> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at org.apache.commons.pool2.impl.LinkedBlockingDeque.takeFirst(LinkedBlockingDeque.java:590)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:425)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:346)
at redis.clients.util.Pool.getResource(Pool.java:49)
at redis.clients.jedis.JedisPool.getResource(JedisPool.java:226)
at redis.clients.jedis.JedisSlotBasedConnectionHandler.getConnectionFromSlot(JedisSlotBasedConnectionHandler.java:70)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:113)
at redis.clients.jedis.JedisClusterCommand.run(JedisClusterCommand.java:30)
at redis.clients.jedis.JedisCluster.set(JedisCluster.java:123)
at slg.rainbow.lock.RedisDistributeLock.tryGetLock(RedisDistributeLock.java:27)
at slg.rainbow.seckill.service.SeckillWebService.submitOrder(SeckillWebService.java:29)
……

上网随便一搜,爆出好几篇文章来,选两个有代表意义的:
《jedisPool.getResource()方法长时间无响应并且不报错》
《jedisPool.getResource()方法长时间无响应并且不报错,方法阻塞》

基本上都是关于JedisPool中MaxWaitMillis参数配置的。如果不对这个参数进行显示配置,那么默认MaxWaitMillis=-1,等于-1什么意思呢?就是当JedisPool线程耗尽的时候,永远的阻塞下去……OMG !

于是我把这个参数调成了1000:
在这里插入图片描述
问题终于解决!

<think> 首先,用户的问题是关于如何使用 jstack 命令获取线程日志。根据系统级指令,我需要: - 所有行内数学表达式用 $...$,但这个问题似乎没有数学相关内容,所以可能不需要。 - 独立公式用 $$...$$,但同样,这里可能不适用。 - LaTeX 语法正确:确保任何公式正确。 - 使用中文回答:整个回复用中文写。 - 生成相关问题:在回答结束后,添加相关问题部分。 - 回答中引用的段落末尾自然地添加引用标识:例如 [^1]。引用来自提供的上下文。 用户提供了两个引用: - 引用 [1]:描述 jstack 工具,用于生成线程快照,帮助定位线程停顿原因。 - 引用 [2]:一步步指导如何使用 jstack 获取线程信息,特别是针对高 CPU 使用率的情况。 用户最后说:"我想获取使用 jstack 命令获取线程日志的方法 请问如何使用 jstack 命令获取线程日志",所以核心是指导如何使用 jstack。 我的回答需要结构清晰,帮助用户逐步解决问题。参考引用来确保真实可靠。 结构化回答: 1. **介绍 jstack**:简要解释 jstack 是什么,基于引用 [1]。 2. **步骤指导**:基于引用 [2],详细描述如何获取线程日志。包括: - 找到高 CPU 使用率的进程 ID。 - 使用 jstack 生成线程快照。 - 分析线程信息。 3. **添加引用标识**:在相关段落末尾添加引用,如 [^1] 和 [^2]。 4. **注意事项**:可能添加一些提示,比如确保 Java 环境设置正确,或解释输出格式。 最后,生成相关问题部分。 相关问题应该基于主题,如: - jstack 的其他用途? - 如何分析 jstack 输出? - 常见问题查技巧? 确保回答真实可靠:依赖提供的引用,避免猜测。 完整回答结构: - 开头:直接回答问题。 - 主体:分步骤解释。 - 结尾:总结。 - 相关问题部分:以 §§相关问题§§ 开头,然后列出问题。 在用户可见层,保持回答清晰、逐步。 </think> ### 如何使用 jstack 命令获取线程日志 `jstack` 是 Java 开发工具包(JDK)中的一个实用命令,用于生成 Java 虚拟机(JVM)的线程快照(也称为线程 dump 或线程日志)。线程快照记录了当前 JVM 中所有线程的执行状态、调用堆栈信息,帮助开发人员诊断线程问题,如死锁、死循环或资源等待。以下是一个结构清晰的逐步指南,基于引用提供的标准操作流程[^1][^2]。操作前,请确保已安装 JDK 并配置好环境变量(如 `JAVA_HOME`)。 #### 步骤 1: 定位高资源消耗的 Java 进程 首先,使用系统工具(如 `top` 或 `jps`)找到目标 Java 进程的 ID(PID)。这通常是 CPU 或内存使用率异常的进程。 - **使用 `top` 命令查看系统进程**: 在终端运行 `top` 命令,查看所有进程的资源占用情况。记录 CPU 或内存占用高的 Java 进程 PID。 - **使用 `jps` 命令确认 Java 进程**: 运行 `jps -lv` 列出所有 Java 进程及其详细信息(包括 PID 和启动参数),结合 `top` 输出确认目标进程 PID。例如: ```bash jps -lv # 输出示例: 12345 com.example.MyApp ``` 如果进程 PID 为 `12345`,则后续步骤使用此 PID。 #### 步骤 2: 监控目标进程的线程 针对目标 PID,使用 `top` 细化监控,找出高资源消耗的线程。 - **运行 `top -p [PID]`**: 替换 `[PID]` 为实际进程 ID,例如 `top -p 12345`。 - **查看线程信息**: 在 `top` 监控界面中,输入 `H`(大写),切换到线程视图。记录 CPU 占用高的线程 ID(TID),例如 `67890`。 #### 步骤 3: 使用 jstack 生成线程快照 使用 `jstack` 命令生成线程日志文件,输出为文本格式。 - **执行 jstack 命令**: 运行以下命令,将快照保存到文件(如 `stack.txt`)。注意:此处使用 Java 进程的 PID(不是线程 ID)。 ```bash jstack [PID] >> stack.txt # 示例: jstack 12345 >> stack.txt ``` 命令执行后,`stack.txt` 文件将包含所有线程的堆栈信息。如果程序崩溃或生成 core 文件,`jstack` 也可用于分析 core dump(添加 `-F` 选项强制附加)[^1]。 #### 步骤 4: 分析线程日志 线程日志文件(如 `stack.txt`)需要进一步分析,以定位问题线程。 - **转换线程 ID 为十六进制**: 高资源消耗的线程 ID(步骤 2 中的 TID)需转换为十六进制格式,以便在日志中搜索。例如: ```bash printf "%x\n" [TID] # 示例: printf "%x\n" 67890 → 输出 "10932" ``` - **在日志文件中查找线程信息**: 打开 `stack.txt`,搜索十六进制线程 ID(如 `nid=0x10932`)。日志会显示该线程的完整堆栈跟踪,包括正在执行的方法和代码行号。 示例日志片段: ``` "main" #1 prio=5 os_prio=0 tid=0x00007f8e3c00a800 nid=0x10932 runnable [0x00007f8e3d88e000] java.lang.Thread.State: RUNNABLE at com.example.MyApp.method(MyApp.java:10) ... ``` 分析堆栈信息,识别问题根源(如死锁或循环调用)[^2]。 #### 注意事项 - **权限要求**:运行 `jstack` 需要与目标 Java 进程相同的用户权限,否则可能失败。 - **实时附加**:如果 Java 进程处于 hung 状态(无响应),`jstack` 可直接附加到运行中进程生成快照[^1]。 - **日志解读**:线程状态(如 `RUNNABLE`、`BLOCKED`)指示问题类型;结合日志和源代码查问题方法。 - **工具扩展**:对于生产环境,可结合 `jcmd` 或可视化工具(如 VisualVM)增强分析。 通过以上步骤,您可以高效获取并分析线程日志,快速定位 JVM 性能问题[^1][^2]。如果遇到权限或环境问题,请检查 JDK 安装路径(`which java` 确认)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值