分析java线程(analyze java thread)

本文详细介绍了Java线程的各种状态及如何使用线程Dump来诊断性能问题,包括死锁、阻塞等情况,并提供了实例分析。

今天看到一篇好的文章,转载下。

-------------------------------------------------------------------------------------------------------------------------------------

当遇到问题或者基于java web的程序跑的比期望中慢,这时候我们需要使用线程dump。如果thread dump对你感觉比较复杂,这篇文章将会帮助你。这里我将先简单介绍一下java线程中的一些简单概念等,然后如何从一个正在跑的程序里面dump thread,之后分析这些线程的各个状态,然后优化性能。

web类服务使用数以百计的线程来处理大量用户的并发请求。如果两个或者更多的线程需要使用相同的资源,这时候就不可避免的出现线程间的竞争或者死锁现象。

线程竞争是指一个线程等待被另外一个线程占用的锁,例如在web应用中,不同的线程要获取共享资源。

线程死锁是指两个或多线线程互相等待对方完成任务,进入了无限的等待中。

为了分析线程的dump信息,需要了解线程的各个状态,下面这张图形象的介绍了几种状态。 

NEW: 线程被创建,但是没有被执行

RUNNABLE: 线程获取到了CPU的分片,处理任务(有可能处于WAITING状态,这取决于操作系统)

BLOCKED:线程等带其他线程释放锁

WAITING:程序调用wait/join方法,使线程处于等待状态

TIMED_WAITING: 线程等待特定的时间,程序调用sleep、wait、join,并带有时间参数

(一)获取线程的dump信息

(1)通过使用JDK自带的jstack获取线程dump信息;

(2)通过可视化工具,例如jconsole、visoleVM来获取

通过可视化工具非常简单,这里不做介绍,线上环境还是用命令行的形式搞定。

1、获取进程的PID,(jps or ps –ef | grep jva  or pgrep java)

2、jstack 打印文件,可以重定向到一个文件保存起来

 

 

Jstack中的参数:

-F 强制进行线程dump,当执行jstack pid没有反应的时候,可以执行此命令

-m 打印java线程还有本地线程

-l 打印线程的锁附加信息

(二)线程信息中各个字段的含义

"pool-1-thread-13" prio=6 tid=0x000000000729a000 nid=0x2fb4 runnable [0x0000000007f0f000] java.lang.Thread.State: RUNNABLE

              at java.net.SocketInputStream.socketRead0(Native Method)

 

        Thread name: When using Java.lang.Thread class to generate a thread, the thread will be named Thread-(Number), whereas when using java.util.concurrent.ThreadFactory class, it will be named pool-(number)-thread-(number).

Priority: 代表线程的优先级

Thread ID: 线程的唯一标示,通过这个ID可以获取一些有用的信息,例如CPU以及内存使用情况

Thread status: 线程的状态

Thread callstack: 方法栈中的信息

线程处于BLOCKED状态

例如下面的信息,线程在等待锁。

 

"BLOCKED_TEST pool-1-thread-2" prio=6 tid=0x0000000007673800 nid=0x260c waiting for monitor entry [0x0000000008abf000]

   java.lang.Thread.State: BLOCKED (on object monitor)

                at com.nbp.theplatform.threaddump.ThreadBlockedState.monitorLock(ThreadBlockedState.java:43)

                - waiting to lock <0x0000000780a000b0> (a com.nbp.theplatform.threaddump.ThreadBlockedState)

                at com.nbp.theplatform.threaddump.ThreadBlockedState$2.run(ThreadBlockedState.java:26)

                at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)

                at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)

                at java.lang.Thread.run(Thread.java:662)

 

   Locked ownable synchronizers:

                - <0x0000000780b0c6a0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)

 

"

(三)死锁的情况

线程A需要获取线程B的锁才能完成它的任务,同时线程B需要获取线程A的锁来进行任务。

例如下面的信息中,

 线程 DEADLOCK_TEST-1 thread 有 0x00000007d58f5e48 这个锁,试图获取这个锁 0x00000007d58f5e60 .

 线程 DEADLOCK_TEST-2 thread 有0x00000007d58f5e60 这个锁,试图获取这个锁 0x00000007d58f5e78 .

 线程DEADLOCK_TEST-3 thread 有 0x00000007d58f5e78 这个锁,试图获取这个锁 0x00000007d58f5e48 l

从而导致死锁。

 

"DEADLOCK_TEST-1" daemon prio=6 tid=0x000000000690f800 nid=0x1820 waiting for monitor entry [0x000000000805f000]

   java.lang.Thread.State: BLOCKED (on object monitor)

                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.goMonitorDeadlock(ThreadDeadLockState.java:197)

                - waiting to lock <0x00000007d58f5e60> (a com.nbp.theplatform.threaddump.ThreadDeadLockState$Monitor)

                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.monitorOurLock(ThreadDeadLockState.java:182)

                - locked <0x00000007d58f5e48> (a com.nbp.theplatform.threaddump.ThreadDeadLockState$Monitor)

                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.run(ThreadDeadLockState.java:135)

 

   Locked ownable synchronizers:

                - None

 

"DEADLOCK_TEST-2" daemon prio=6 tid=0x0000000006858800 nid=0x17b8 waiting for monitor entry [0x000000000815f000]

   java.lang.Thread.State: BLOCKED (on object monitor)

                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.goMonitorDeadlock(ThreadDeadLockState.java:197)

                - waiting to lock <0x00000007d58f5e78> (a com.nbp.theplatform.threaddump.ThreadDeadLockState$Monitor)

                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.monitorOurLock(ThreadDeadLockState.java:182)

                - locked <0x00000007d58f5e60> (a com.nbp.theplatform.threaddump.ThreadDeadLockState$Monitor)

                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.run(ThreadDeadLockState.java:135)

 

   Locked ownable synchronizers:

                - None

 

"DEADLOCK_TEST-3" daemon prio=6 tid=0x0000000006859000 nid=0x25dc waiting for monitor entry [0x000000000825f000]

   java.lang.Thread.State: BLOCKED (on object monitor)

                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.goMonitorDeadlock(ThreadDeadLockState.java:197)

                - waiting to lock <0x00000007d58f5e48> (a com.nbp.theplatform.threaddump.ThreadDeadLockState$Monitor)

                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.monitorOurLock(ThreadDeadLockState.java:182)

                - locked <0x00000007d58f5e78> (a com.nbp.theplatform.threaddump.ThreadDeadLockState$Monitor)

                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.run(ThreadDeadLockState.java:135)

 

   Locked ownable synchronizers:

                - None

(四)程序反常的比较慢时,如何来处理?

在多次dump之后,发现有以下几个线程处于阻塞状态

" DB-Processor-13" daemon prio=5 tid=0x003edf98 nid=0xca waiting for monitor entry [0x000000000825f000]

java.lang.Thread.State: BLOCKED (on object monitor)

                at beans.ConnectionPool.getConnection(ConnectionPool.java:102)

                - waiting to lock <0xe0375410> (a beans.ConnectionPool)

                at beans.cus.ServiceCnt.getTodayCount(ServiceCnt.java:111)

                at beans.cus.ServiceCnt.insertCount(ServiceCnt.java:43)

 

"DB-Processor-14" daemon prio=5 tid=0x003edf98 nid=0xca waiting for monitor entry [0x000000000825f020]

java.lang.Thread.State: BLOCKED (on object monitor)

                at beans.ConnectionPool.getConnection(ConnectionPool.java:102)

                - waiting to lock <0xe0375410> (a beans.ConnectionPool)

                at beans.cus.ServiceCnt.getTodayCount(ServiceCnt.java:111)

                at beans.cus.ServiceCnt.insertCount(ServiceCnt.java:43)

 

" DB-Processor-3" daemon prio=5 tid=0x00928248 nid=0x8b waiting for monitor entry [0x000000000825d080]

java.lang.Thread.State: RUNNABLE

                at oracle.jdbc.driver.OracleConnection.isClosed(OracleConnection.java:570)

                - waiting to lock <0xe03ba2e0> (a oracle.jdbc.driver.OracleConnection)

                at beans.ConnectionPool.getConnection(ConnectionPool.java:112)

                - locked <0xe0386580> (a java.util.Vector)

                - locked <0xe0375410> (a beans.ConnectionPool)

                at beans.cus.Cue_1700c.GetNationList(Cue_1700c.java:66)

                at org.apache.jsp.cue_1700c_jsp._jspService(cue_1700c_jsp.java:120)

从上面的信息中可以看出,线程阻塞在获取数据库连接,这时候分析可能有两个原因:

第一个是错误的配置;

第二个是反常的数据库连接;

 

(五)为了方便排查问题,编写多线程代码的时候,最好对线程进行命名

 

(六)通过使用Mbean获取更多的线程信息

核心的代码如下:

ThreadMXBean mxBean = ManagementFactory.getThreadMXBean();

long[] threadIds = mxBean.getAllThreadIds();

ThreadInfo[] threadInfos =mxBean.getThreadInfo(threadIds);

 

(七)处理CPU利用率突然特别高的问题,看看CPU最高的那个线程在做什么

例子:查看特定java线程的情况:

(一)通过top命令,查看消耗内存较高的线程的id,

         运行top,shift+h来显示所有进程的信息,包含进程中线程的情况,从中找出占用内存最大的那个java线程。

         也可直接运行:top -H -p  pid  来显示java进程中的各个线程的情况(p是显示特定的进程号,H是显示进程中的所有线程)

(二)进制间的转换:

         printf '0x%x\n'   pid

         获取线程ID的另外一中展现形式

(三)dump线程,然后搜搜索线程为上述ID的线程的信息

总结:

对于这篇文章,我主要对于开发者介绍了一些关于多线程处理的经验,这些对于一些经验丰富的开发者来说可能是一些简单的知识。我感觉对于线程没有提供丰富的背景信息,而是直接介绍了thread dump,我希望这篇文章能够给更多的开发者带来帮助。

转载于:http://iamzhongyong.iteye.com/blog/1441285。

<think>好的,我现在需要解决用户关于Java HPROF文件分析的问题。用户希望我以编程专家的身份,详细解答并提供解决方案,包括代码示例和解释,最后提出相关问题。首先,我需要回忆HPROF文件的相关知识,确定分析HPROF文件的常用方法和工具。 首先,HPROF文件Java堆转储文件,用于分析内存泄漏或对象分布。常见的工具有jhat、EclipseMAT和VisualVM。用户可能需要知道如何生成HPROF文件,以及如何使用这些工具进行分析。此外,可能还需要编写代码示例,比如如何通过Java命令生成HPROF,或者用脚本处理分析结果。 接下来,我需要考虑如何分步骤解答。第一步是生成HPROF文件,通常通过JVM参数,比如-XX:+HeapDumpOnOutOfMemoryError。然后,分析工具的使用,比如用jhat启动服务器查看,或者Eclipse MAT的图形化界面。是否需要代码示例?比如,一个触发内存泄漏的Java程序,然后展示如何生成转储文件。或者用shell命令调用分析工具。 然后,解释每个步骤的作用,比如不同的工具优缺点,jhat可能比较基础,而MAT更强大。还要注意不同工具的使用方法,比如jhat的命令行参数,MAT需要打开文件等。 可能还需要考虑自动化分析的部分,比如用jhat的API或其他库解析HPROF文件,但通常这可能比较复杂,所以重点还是放在工具的使用上。因此,代码示例可能包括如何生成HPROF,比如一个Java程序故意引发内存泄漏,并配置JVM参数来生成堆转储。在编写代码示例时,要确保正确性。例如,一个Java类不断向列表中添加字符串,导致OOM,并设置HeapDumpOnOutOfMemoryError参数。然后,用命令行运行这个类,生成HPROF文件。接着,用jhat或MAT分析。 然后,解释代码中的关键点,比如如何设置JVM参数,程序的行为,以及生成的文件位置。之后,详细说明分析步骤,比如使用jhat启动后如何通过浏览器查看,或者如何用MAT查找支配树或泄漏嫌疑。 最后,提出相关问题,可能涉及其他工具、HPROF文件结构、内存泄漏常见模式、自动化分析脚本等。 现在需要检查是否有遗漏的部分,比如其他生成HPROF的方法(使用jmap命令),或者不同的分析工具比较。确保覆盖用户可能关心的各个方面,并提供足够的细节,让用户能够按照步骤操作。</think>Java HPROF文件是JVM生成的内存堆转储文件,主要用于分析内存泄漏和对象内存分布。以下是完整的分析方法和工具使用指南: 1. **生成HPROF文件**: ```java // 内存泄漏示例代码 import java.util.ArrayList; import java.util.List; public class MemoryLeakDemo { static List<Object> leakList = new ArrayList<>(); public static void main(String[] args) { while (true) { leakList.add(new byte[1024 * 1024]); // 每次添加1MB try { Thread.sleep(100); } catch (InterruptedException e) {} } } } ``` 运行命令: ```bash java -XX:+HeapDumpOnOutOfMemoryError -Xmx100m MemoryLeakDemo ``` 当出现OOM时,会自动生成java_pid<pid>.hprof文件 2. **使用工具分析**: **jhat基础分析**: ```bash jhat -J-Xmx2G java_pid12345.hprof ``` 访问http://localhost:7000查看: - 直方图:展示类实例数量和占用内存 - 执行QL查询:`select referrers(s) from java.lang.String s` **Eclipse MAT深度分析**: 1. 安装MAT插件 2. 打开HPROF文件 3. 关键功能: - Leak Suspects Report(泄漏嫌疑报告) - Dominator Tree(支配树) - Path to GC Roots(到GC根的路径) 3. **自动化分析脚本**: ```python import subprocess import webbrowser def analyze_hprof(hprof_path): # 使用jhat自动分析 port = 7000 process = subprocess.Popen(["jhat", "-J-Xmx4G", hprof_path]) # 等待服务启动 import time time.sleep(10) # 打开浏览器 webbrowser.open(f"http://localhost:{port}") if __name__ == "__main__": analyze_hprof("java_pid12345.hprof") ``` **关键分析步骤**: 1. 识别大对象:通过直方图找到占用内存最多的类 2. 查看GC Root:确认为何对象无法被回收 3. 比较堆转储:在不同时间点获取多个dump对比分析 4. 检查线程栈:查看对象创建路径
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值