android中的jstack,看看线程都在干嘛

本文介绍如何在Android环境中使用adb命令获取指定进程的Java线程Dump,这对于诊断应用无响应等问题非常有用。文章提供了具体的命令示例及解析。
之前做java开发的时候,遇到进程卡住的情况都会用jstack来打印一个进程里的线程活动情况。到了安卓开发,发现没有这个命令了,很不习惯。
google了一下,发现还是有办法的。

% adb shell ps | grep android.calendar
u0_a6 2596 127 912804 48296 ffffffff b6f62c10 S com.google.android.calendar

2596就是进程ID
% adb shell kill -3 2596

成功的话logcat里可以看到下面这样的输出:
[quote]I/dalvikvm( 2596): Wrote stack traces to '/data/anr/traces.txt'[/quote]
如果没有/data/anr 可能会失败。需要手动建一个目录

用adb把文件拉下来
[quote]% adb pull /data/anr/traces.txt .[/quote]

线程DUMP是以附加的形式打到 traces.txt上面的。所以要根据PID来确认。

这个对于 排查程序无响应很有帮助。

参考:http://stackoverflow.com/questions/13589074/how-to-make-java-thread-dump-in-android


打印出来的文件大致如下:[quote]
----- pid 155 at 2013-12-11 20:38:16 -----
Cmd line: system_server

DALVIK THREADS:
(mutexes: tll=0 tsl=0 tscl=0 ghl=0)
"main" prio=5 tid=1 NATIVE
| group="main" sCount=1 dsCount=0 obj=0x40a3d460 self=0x12800
| sysTid=155 nice=0 sched=0/0 cgrp=default handle=1074083080
| schedstat=( 296672000 85598000 170 ) utm=19 stm=10 core=0
at com.android.server.SystemServer.init1(Native Method)
at com.android.server.SystemServer.main(SystemServer.java:1103)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:795)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:562)
at dalvik.system.NativeStart.main(Native Method)

"DhcpStateMachine" prio=5 tid=66 NATIVE
| group="main" sCount=1 dsCount=0 obj=0x411a3b30 self=0x18dfa0
| sysTid=443 nice=0 sched=0/0 cgrp=default handle=1660872
| schedstat=( 3046000 48091000 54 ) utm=0 stm=0 core=0
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:118)
at android.os.Looper.loop(Looper.java:118)
at android.os.HandlerThread.run(HandlerThread.java:60)
[/quote]
<think>首先,用户的问题是:"我想查找使用jstack发现大量阻塞线程后的解决方案",意思是用户想知道当使用jstack工具发现有大量阻塞线程时,应该采取什么样的解决办法。 根据系统级指令,我需要使用中文回答,结构清晰,逐步解决问题,并保证回答真实可靠。同时,需要生成相关问题,并在末尾添加引用标识。 回顾用户提供的引用: - 引用[1]:描述了线程状态,如RUNNABLE、BLOCKED等。 - 引用[2]:列出了jstack日志中的状态,如死锁、等待资源、等待监视器等。 - 引用[3]:类似地描述了线程状态RUNNABLE、BLOCKED、WAITING等。 - 引用[4]:提供了一个死锁的例子和解决方案。 用户的问题是针对"大量阻塞线程"的解决方案,所以我需要聚焦在BLOCKED状态线程的处理上。 关键点: - BLOCKED状态通常表示线程在等待获取锁,比如synchronized块。 - 大量阻塞线程可能表明代码中存在锁竞争、死锁或资源瓶颈。 解决方法应该包括诊断和解决步骤: 1. **识别阻塞原因**:使用jstack分析线程堆栈,找出哪些线程在阻塞,以及它们在等待什么锁。 2. **解决常见问题**: - **死锁**:如果检测到死锁(如引用[4]所示),需要打破循环等待。修改代码以避免相互依赖的锁。 - **锁竞争**:如果多个线程竞争同一资源,考虑使用锁优化,如减小锁范围、使用读写锁或避免不必要的同步。 - **资源等待**:如果是等待外部资源(如数据库或网络),优化资源访问或增加资源。 3. **优化建议**:代码审查、使用并发工具类(如java.util.concurrent包中的类)、性能测试等。 确保回答结构清晰: - 引言:简要解释问题。 - 步骤:列出诊断和解决步骤。 - 总结:关键要点。 最后,生成相关问题。 引用标识:在回答中引用的地方添加[^1]、[^2]、[^3]、[^4]。既然用户提供了这些引用,我应该适当引用它们来支持我的回答。 例如: - 提到线程状态时引用[1]或[3]。 - 谈到死锁解决方法时引用[4]。 内容大纲: 1. **理解阻塞线程**:解释BLOCKED状态的含义,引用[1]或[3]。 2. **诊断工具**:如何使用jstack分析线程堆栈。 3. **常见原因和解决方案**: - 死锁:检测和解决,引用[4]。 - 锁竞争:优化锁使用。 - 其他等待:如等待资源(WAITING状态)。 4. **优化策略**:代码改进、并发工具使用。 5. **预防措施**:避免未来问题。 生成相关问题:在末尾添加3-4个相关问题。 回答格式: - 使用中文。 - 行内数学表达式用$...$,但这里可能不需要数学表达式,专注于文本。 - 独立公式用$$...$$,同样可能不需要。 确保真实可靠:基于Java并发知识。 </think>### 使用jstack发现大量阻塞线程后的解决方案 当使用jstack工具检测到大量线程处于BLOCKED状态时,这表明线程在等待获取锁(如synchronized块或其他并发锁),导致系统性能下降或死锁风险增加。解决此类问题需要结构化诊断和优化,以下是逐步解决方案。本回答基于Java并发原理和jstack日志分析[^1][^3],并结合实际最佳实践。 #### 1. **诊断阻塞原因:分析jstack日志** - **步骤**: - 运行jstack命令导出线程堆栈:`jstack -l <pid> > thread_dump.txt`(`<pid>`为Java进程ID)。 - 在日志中搜索`BLOCKED`状态线程,重点关注线程堆栈中的锁信息(如锁对象和等待位置)。 - 检查是否有死锁迹象:如果多个线程相互等待对方持有的锁(例如,线程A持有锁X等待锁Y,线程B持有锁Y等待锁X),则出现死锁[^4]。 - 分析阻塞线程的调用栈:确定哪些资源(如数据库连接、共享对象)导致阻塞,并统计阻塞频率。 - **工具辅助**: - 使用visualvm、jconsole或在线工具分析jstack日志可视化线程依赖。 - 结合日志状态(如`Waiting on monitor entry`或`Object.wait()`),区分锁竞争、资源等待或死锁[^2]。 #### 2. **解决常见原因** - **如果检测到死锁(Deadlock)**: - **解决方案**:修改代码打破循环依赖。 - 示例:避免持有多个锁的顺序不一致(如引用[4]所述的两个对象相互引用问题)。使用`tryLock`或锁超时避免无限等待。 - 代码优化示例: ```java // 原代码:易死锁的互斥调用 synchronized void methodA(Object obj) { synchronized(obj) { /* ... */ } // 嵌套锁易死锁 } // 修改后:使用统一锁顺序 void methodA(Object obj1, Object obj2) { Object firstLock = obj1.hashCode() < obj2.hashCode() ? obj1 : obj2; Object secondLock = firstLock == obj1 ? obj2 : obj1; synchronized(firstLock) { synchronized(secondLock) { /* ... */ } // 固定锁顺序减少风险 } } ``` - 运行jstack验证死锁是否消除[^4]。 - **工具**:使用jstack的`-m`选项自动检测死锁,或通过Java Mission Control实时监控。 - **如果检测到锁竞争(Lock Contention)**: - **原因**:多个线程竞争同一资源(如全局静态变量或高并发方法)。 - **解决方案**: - **减小锁粒度**:将粗粒度锁替换为细粒度锁(如使用ConcurrentHashMap替代synchronized Map)。 - **使用并发工具**:采用java.util.concurrent包中的类,如ReentrantLock、Semaphore或ReadWriteLock,支持公平锁和超时控制。 - **避免锁泄漏**:确保锁在finally块中释放。 - **优化代码**:减少同步块范围,或使用无锁数据结构(如AtomicInteger)。 - **示例**: ```java // 原代码:全局锁导致阻塞 private static final Object lock = new Object(); public void highConcurrencyMethod() { synchronized(lock) { // 所有线程竞争同一锁 // ... } } // 修改后:使用局部锁或并发类 private final ReentrantLock lock = new ReentrantLock(); public void highConcurrencyMethod() { if (lock.tryLock(100, TimeUnit.MILLISECONDS)) { // 添加超时 try { /* ... */ } finally { lock.unlock(); } // 确保释放 } } ``` - **如果线程在等待资源(Waiting on condition)**: - **原因**:线程等待外部资源(如网络IO、数据库响应),而非锁竞争[^2]。 - **解决方案**: - **优化资源访问**:增加连接池大小、优化SQL查询或使用异步IO(如CompletableFuture)。 - **设置超时**:在等待操作中加入超时机制(如Thread.join(timeout)或Object.wait(timeout))。 - **监控外部依赖**:检查数据库、网络延迟,确保资源可用性。 #### 3. **系统级优化和预防措施** - **性能调优**: - 增加JVM线程池大小(如Tomcat的maxThreads参数),避免线程饥饿。 - 监控系统资源(CPU、内存)使用jstat或GC日志,确保非线程问题引起的阻塞。 - 定期运行jstack(如每5分钟)对比日志,跟踪阻塞趋势。 - **代码审查和测试**: - 使用静态分析工具(如FindBugs或SonarQube)检测潜在死锁。 - 压力测试:模拟高并发场景,使用JMeter或Gatling验证优化效果。 - 优先使用高级并发模型:如Akka或Reactive Streams避免阻塞操作。 - **预防策略**: - 避免过度同步:只在必要时使用锁。 - 日志记录:添加日志输出锁获取情况,便于事后分析。 - 培训开发人员:遵循Java并发最佳实践(如Java Concurrency in Practice指南)。 #### 总结关键要点 大量阻塞线程通常源于锁竞争或死锁,需通过jstack诊断具体状态(BLOCKED或Waiting on monitor entry)后针对性解决[^1][^3]。优先优化锁机制,并使用并发工具降低风险。预防是关键,通过代码审查和监控避免问题复发。若阻塞由外部资源引起,需跨系统协作优化[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值