当有障碍,或者是一个基于 JAVA 的 WEB 应用运行的比预期慢的时候,我们需要使用 thread dumps
。如果对于你来说,thread dumps
是非常复杂的,这篇文章或许能对你有所帮助。在这里我将解释在 JAVA 中什么是 threads
,他们的类型,怎么被创建的,怎样管理它们,你怎样从正在运行的应用中 dump threads
,最后你可以怎样分析它以及确定瓶颈或者是阻塞线程。本文来自于 JAVA 应用程序长期调试经验的结果。
Java and Thread
一个 web 服务器使用几十到几百个线程来处理大量并发用户,如果一个或多个线程使用相同的资源,线程之间的竞争就不可避免了,并且有时候可能会发生死锁。
Thread contention 是一个线程等待锁的一个状态,这个锁被另外一个线程持有,等待被释放,不同的线程频繁访问 WEB 应用的共享资源。例如,记录一条日志,线程尝试记录日志之前必须先获取锁来访问共享资源。
死锁是线程竞争的一个特殊状态,一个或是多个线程在等待其他线程完成它们的任务为了完成它们自己的任务。
线程竞争会引起各种不同的问题,为了分析这些这些问题,你需要使用 dump threads,dump threads
能给你提供每个线程的精确状态信息。
JAVA 线程的背景资料
线程同步
一个线程可以与其他线程在同一时间内被处理。为了确保一致性,当多个线程试图使用共享资源的时候,通过使用 hread synchronization
在同一时间内,应该只有一个线程能访问共享资源
JAVA 中的线程同步可以使用监视器,每个 JAVA 对象都有一个单独的监视器,这个监视器仅仅只能被一个线程拥有,对于拥有一个由不同的线程所拥有的监视器的线程,确实需要在队列中等待,以便其他线程释放它的监视器。
线程状态
为了分析一个 thread dump
文件,你需要知道线程状态。线程情况在 java.lang.Thread.State
中阐明了。
图1:线程状态
- NEW:线程刚被创建,但是还没有被处理。
- RUNNABLE:线程占用了 CPU 并且处理了一个任务。(或是是在等待状态由于操作系统的资源分配)
- BLOCKED:该线程正在等待另外的不同的线程释放锁,以便获取监视器锁
- WAITING:该线程正在等待,通过使用了 wait, join 或者是 park 方法
- TIMED_WAITING:该线程正在等待,通过使用了 sleep, wait, join 或者是 park 方法。(这个与
WAITING
不同是通过方法参数指定了最大等待时间,WAITING
可以通过时间或者是外部的变化解除)
线程类型
JAVA 的线程类型分为以下两种:
- daemon threads;
- 非 daemon threads。
Daemon threads 将停止工作当没有其他任何非 Daemon threads
时。即使你不创建任何线程,JAVA 应用也将默认创建几个线程。他们大部分是 daemon threads
。主要用于任务处理比如内存回收或者是 JMX
。
一个运行 static void main(String[] args)
方法的线程被作为非 daemon threads
线程创建,并且当该线程停止工作的时候,所有任何其他 daemon threads
也将停止工作。(这个运行在 main 方法中的线程被称为 VM thread in HotSpot VM)
获取一个 Thread Dump
我们将介绍三种最常用的方法,记住,有非常多的其他方法可以获取thread dump
,一个 thread dump
仅仅只能在测量的时候显示线程状态。因此为了看得线程状态的变化,建议每隔5秒提取5到10次的记录。
使用 jstack 获取 Thread Dump
在 JDK1.6 或者是更高的版本中,通过使用 jstack, 在 MS Windows 平台上可能可以获取到 Thread Dump
。
通过使用 jps
检查当前正在运行的JAVA进程的 PID。
[user@linux ~]$ jps -v
25780 RemoteTestRunner -Dfile.encoding=UTF-8
25590 sub.rmi.registry.RegistryImpl 2999 -Dapplication.home=/home1/user/java/jdk.1.6.0_24 -Xms8m
26300 sun.tools.jps.Jps -mlvV -Dapplication.home=/home1/user/java/jdk.1.6.0_24 -Xms8m
使用明确的 PID 作为 jstack
的参数来获取 thread dumps
。
[user@linux ~]$ jstack -f 5824
使用 jVisualVM 生成 Thread Dump
通过使用一个程序 jVisualVM
来生成 Thread Dump
。
如上图在左侧的任务表示当前正在运行的进程列表,点击你想要信息的那个线程,然后选择 thread tab
页来检查实时的线程信息。点击右边的 Thread Dump
按钮来获取 thread dump
文件。
在 Linux 控制台生成
通过使用 ps -ef
命令来获取当前正在运行的 JAVA 应用程序的进程 ID。
[user@linux ~]$ ps - ef | grep java
user 2477 1 0 Dec23 ? 00:10:45 ...
user 25780 25361 0 15:02 pts/3 00:00:02 ./jstatd -J -Djava.security.policy=jstatd.all.policy -p 2999
user 26335 25361 0 15:49 pts/3 00:00:00 grep java
使用精确的 pid 作为 kill –SIGQUIT(3)
的参数来获取 thread dump
。
Thread Dump 文件的 线程信息
"pool-1-thread-13" prio=6 tid=0x000000000729a000 nid=0x2fb4 runnable [0x0000000007f0f000] java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:129)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158)
- locked <0x0000000780b7e688> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:167)
at java.io.BufferedReader.fill(BufferedReader.java:136)
at java.io.BufferedReader.readLine(BufferedReader.java:299)
- locked <0x0000000780b7e688> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:362)
- 线程名字:当使用
Java.lang.Thread
类生成一个线程的时候,该线程将被命名为Thread-(Number)
。但是当使用java.util.concurrent.ThreadFactory
类的时候,它将被命名为pool-(number)-thread-(number)
。 - 优先级:代表该线程的优先级
- 线程 ID:代表该线程的唯一 ID,(一些有用的信息,比如该线程的 CPU 使用率或者是内存使用率,都能通过该线程 ID 获取到)。
- 线程状态:代表该线程当前的状态
- 线程调用栈:代表该线程的调用栈信息
Thread Dump Patterns by Type When Unable to Obtain a Lock (BLOCKED)
这个应用程序的整体性能下降是因为一个线程占用了锁阻止了其他线程获得锁,在下面的示例中,BLOCKED_TEST pool-1-thread-1
线程占用了 <0x0000000780a000b0>
锁,然而 BLOCKED_TEST pool-1-thread-2
和 BLOCKED_TEST pool-1-thread-3 threads
正在等待获取锁。
"BLOCKED_TEST pool-1-thread-1" prio=6 tid=0x0000000006904800 nid=0x28f4 runnable [0x000000000785f000]
java.lang.Thread.State: RUNNABLE
at java.io.FileOutputStream.writeBytes(Native Method)
at java.io.FileOutputStream.write(FileOutputStream.java:282)
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:65)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:123)
- locked <0x0000000780a31778> (a java.io.BufferedOutputStream)
at java.io.PrintStream.write(PrintStream.java:432)
- locked <0x0000000780a04118> (a java.io.PrintStream)
at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:202)
at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:272)
at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:85)
- locked <0x0000000780a040c0> (a java.io.OutputStreamWriter)
at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:168)
at java.io.PrintStream.newLine(PrintStream.java:496)
- locked <0x0000000780a04118> (a java.io.PrintStream)
at java.io.PrintStream.println(PrintStream.java:687)
- locked <0x0000000780a04118> (a java.io.PrintStream)
at com.nbp.theplatform.threaddump.ThreadBlockedState.monitorLock(ThreadBlockedState.java:44)
- locked <0x0000000780a000b0> (a com.nbp.theplatform.threaddump.ThreadBlockedState)
at com.nbp.theplatform.threaddump.ThreadBlockedState$1.run(ThreadBlockedState.java:7)
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:
- <0x0000000780a31758> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
"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)
"BLOCKED_TEST pool-1-thread-3" prio=6 tid=0x00000000074f5800 nid=0x1994 waiting for monitor entry [0x0000000008bbf000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.nbp.theplatform.threaddump.ThreadBlockedState.monitorLock(ThreadBlockedState.java:42)
- waiting to lock <0x0000000780a000b0> (a com.nbp.theplatform.threaddump.ThreadBlockedState)
at com.nbp.theplatform.threaddump.ThreadBlockedState\$3.run(ThreadBlockedState.java:34)
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:
- <0x0000000780b0e1b8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
当在死锁状态
这是当线程 A 需要获取线程 B 的锁来继续它的任务,然而线程 B 也需要获取线程 A 的锁来继续它的任务的时候发生的。在thread dump
中,你能看到 DEADLOCK_TEST-1
线程持有 0x00000007d58f5e48
锁,并且尝试获取 0x00000007d58f5e60
锁。你也能看到 DEADLOCK_TEST-2
线程持有 0x00000007d58f5e60
,并且尝试获取 0x00000007d58f5e78
,同时 DEADLOCK_TEST-3
线程持有 0x00000007d58f5e78
,并且在尝试获取 0x00000007d58f5e48
锁,如你所见,每个线程都在等待获取另外一个线程的锁,这状态将不会被改变直到一个线程丢弃了它的锁。
"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
当持续等待从远处服务器接收消息
该线程是正常的,因为它的状态为 RUNNABLE,尽管如此,当你按照时间顺序排列 Thread Dump
,你会发现 socketReadThread
线程正在无限等待读取 socket。
"socketReadThread" prio=6 tid=0x0000000006a0d800 nid=0x1b40 runnable [0x00000000089ef000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:129)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158)
- locked <0x00000007d78a2230> (a java.io.InputStreamReader)
at sun.nio.cs.StreamDecoder.read0(StreamDecoder.java:107)
- locked <0x00000007d78a2230> (a java.io.InputStreamReader)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:93)
at java.io.InputStreamReader.read(InputStreamReader.java:151)
at com.nbp.theplatform.threaddump.ThreadSocketReadState$1.run(ThreadSocketReadState.java:27)
at java.lang.Thread.run(Thread.java:662)
当 Waiting 时
线程保持在 Waiting
状态,在 Thread Dump
中,IoWaitThread
线程保持等待状态来从 LinkedBlockingQueue
接收消息。如果 LinkedBlockingQueue
一直没有消息,该线程的状态将不会改变。
"IoWaitThread" prio=6 tid=0x0000000007334800 nid=0x2b3c waiting on condition [0x000000000893f000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000007d5c45850> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:156)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1987)
at java.util.concurrent.LinkedBlockingDeque.takeFirst(LinkedBlockingDeque.java:440)
at java.util.concurrent.LinkedBlockingDeque.take(LinkedBlockingDeque.java:629)
at com.nbp.theplatform.threaddump.ThreadIoWaitState$IoWaitHandler2.run(ThreadIoWaitState.java:89)
at java.lang.Thread.run(Thread.java:662)
当线程的资源不能正常的被组织
不必要的线程会堆积起来,当线程的资源不能被正常的组织的话,如果这个发送了,建议监控线程组织过程或检查线程终止的条件。
使用 Thread Dump 怎样解决问题
示例1:当 CPU 利用率高的异常
- 提取获取最高 CPU 使用率的线程。
[user@linux ~]$ ps -mo pid.lwp.stime.time.cpu -C java
PID LWP STIME TIME %CPU
10029 - Dec07 00:02:02 99.5
- 10039 Dec07 00:00:00 0.1
- 10040 Dec07 00:00:00 95.5
从这个应用中,发现使用 CPU 最高的线程。
获取使用 CPU 最多的轻量级进程(LWP),把它的唯一标示码 (10039) 转换成十六进制 (0x2737)。
- 然后获取进程的
Thread Dump
,检查进程的动作。
通过 PID 10029 来提取应用程序的 Thread Dump
,然后通过一个 nid 0x2737 来找到这个线程。
"NioProcessor-2" prio=10 tid=0x0a8d2800 nid=0x2737 runnable [0x49aa5000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:210)
at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:65)
at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:69)
- locked <0x74c52678> (a sun.nio.ch.Util$1)
- locked <0x74c52668> (a java.util.Collections$UnmodifiableSet)
- locked <0x74c501b0> (a sun.nio.ch.EPollSelectorImpl)
at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:80)
at external.org.apache.mina.transport.socket.nio.NioProcessor.select(NioProcessor.java:65)
at external.org.apache.mina.common.AbstractPollingIoProcessor$Worker.run(AbstractPollingIoProcessor.java:708)
at external.org.apache.mina.util.NamePreservingRunnable.run(NamePreservingRunnable.java:51)
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)
每个小时的几个时间提取 Thread Dump
,然后检查线程的状态来确定问题。
示例2:当进程的性能异常的慢
多次获得 thread dumps
后,找出 BLOCKED
状态的线程列表。
" 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)
在多次获取 thread dumps
后,取得 BLOCKED
状态的线程列表。
如果线程是 BLOCKED
的,提取线程尝试获取的相关联的锁。
通过 thread dumps
,你能确定线程状态停止在 BLOCKED
,因为锁 <0xe0375410>
不能被获取到,这个问题可以通过分析当前夯住的线程的 stack trace
来解决。
使用 DBMS
的时候,为什么以上的范例经常出现再应用程序中,这有两个原因。第一个原因是配置不当。尽管事实是该线程仍然在工作,它们不能展示它们最好的性能,因为 DBCP
的配置文件没有配置正确。如果你多次提取 thread dumps
并且对比它们,你将经常看到被阻塞的线程之前处于不同的状态。
第二个原因是不正常的连接。当与 DBMS
的连接保持在不正常的状态,线程将等待直到超时。在这个例子中,通过多次提取 thread dumps
并对比它们,你会发现与 DBMS 相关的线程仍然在阻塞状态。通过适当改变一些值,比如超时时间,你可以缩短问题发生的时间。
为简单的 Thread Dump 命名线程编码
当使用 java.lang.Thread
对象创建线程的时候,线程被命名为 Thread-(Number) 。当使用 java.util.concurrent.DefaultThreadFactory
对象创建线程的时候,线程被命名为 named pool-(Number)-thread-(Number)。当为应用程序分析成百上千的线程的时候,如果线程依然用它们默认的名字,分析它们将变得非常困难,因为这是非常难以辨别这些线程来分析的。
因此,你被建议开发一个命名线程的规则当一个新线程被创建的时候。
当你使用 java.lang.Thread
创建线程,你可以通过创建参数给该线程定义个约定俗成的名字。
public Thread(Runnable target, String name);
public Thread(ThreadGroup group, String name);
public Thread(ThreadGroup group, Runnable target, String name);
public Thread(ThreadGroup group, Runnable target, String name, long stackSize);
当你使用 java.util.concurrent.ThreadFactory
创建线程的时候,你可以通过生成你自己的线程工厂来命名它,如果你不需要特别的功能性,你可以使用 MyThreadFactory
作为以下描述:
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
public class MyThreadFactory implements ThreadFactory {
private static final ConcurrentHashMap<String, AtomicInteger> POOL_NUMBER =
new ConcurrentHashMap<String, AtomicInteger>();
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
public MyThreadFactory(String threadPoolName) {
if (threadPoolName == null) {
throw new NullPointerException("threadPoolName");
}
POOL_NUMBER.putIfAbsent(threadPoolName, new AtomicInteger());
SecurityManager securityManager = System.getSecurityManager();
group = (securityManager != null) ? securityManager.getThreadGroup() :
Thread.currentThread().getThreadGroup();
AtomicInteger poolCount = POOL_NUMBER.get(threadPoolName);
if (poolCount == null) {
namePrefix = threadPoolName + " pool-00-thread-";
} else {
namePrefix = threadPoolName + " pool-" + poolCount.getAndIncrement() + "-thread-";
}
}
public Thread newThread(Runnable runnable) {
Thread thread = new Thread(group, runnable, namePrefix + threadNumber.getAndIncrement(), 0);
if (thread.isDaemon()) {
thread.setDaemon(false);
}
if (thread.getPriority() != Thread.NORM_PRIORITY) {
thread.setPriority(Thread.NORM_PRIORITY);
}
return thread;
}
}
使用 MBean 获取更多的细节信息
你可以使用 MBean 来获取 ThreadInfo
对象。你也可以获取更加多通过 thread dumps 不能获取的信息。通过使用 ThreadInfo
。
ThreadMXBean mxBean = ManagementFactory.getThreadMXBean();
long[] threadIds = mxBean.getAllThreadIds();
ThreadInfo[] threadInfos =
mxBean.getThreadInfo(threadIds);
for (ThreadInfo threadInfo : threadInfos) {
System.out.println(
threadInfo.getThreadName());
System.out.println(
threadInfo.getBlockedCount());
System.out.println(
threadInfo.getBlockedTime());
System.out.println(
threadInfo.getWaitedCount());
System.out.println(
threadInfo.getWaitedTime());
}
你可以使用方法 ThreadInfo
来提取阻塞线程或者是等待线程花费的时间。并利用这一点,你也可以得到那些处于非活动状态的时间异常长的线程列表。
相关推荐
5. **日志分析**:jca467.jar可能提供日志记录功能,将线程堆栈信息输出到文件,便于后续分析和问题排查。 6. **非侵入式监控**:这个工具通常不会影响到应用程序的正常运行,可以在生产环境中安全地收集数据。 ...
在分析Java应用程序的性能问题时,了解和分析Java线程的状态是非常关键的。线程堆栈信息可以让我们深入观察到线程的运行状况,包括线程的当前状态、调用堆栈、锁信息等。为了获取这些信息,我们可以使用jstack工具,...
Java线程分析是Java开发中的重要环节,尤其是在处理性能优化、死锁排查或者并发问题时。TDA(Thread Dump Analyzer)是一款强大的Java线程分析工具,它能够帮助开发者深入理解应用在运行时的线程状态,包括线程的...
分析`jstack`输出的线程堆栈,我们需要重点关注那些长时间运行或重复执行的线程,它们可能是CPU资源占用高的主要原因。常见的问题可能包括无限循环、锁竞争、计算密集型任务没有合理优化等。针对发现的问题,我们...
2. **理解日志结构**:javacore日志由多个部分组成,包括JVM概览、线程堆栈跟踪、类加载信息、内存区域分配等。每个部分都为诊断提供了关键信息。 3. **识别问题迹象**:内存溢出通常会在堆分配、永久代或元空间...
Java线程Dump分析工具jstack是Java开发人员和运维人员常用的诊断工具,它能够帮助我们了解Java应用程序中线程的状态,以及线程的执行轨迹。本文将深入解析jstack的使用方法及其在不同场景下的应用。 jstack命令的...
本篇将围绕“使用Java正则表达式分析处理日志”这一主题,探讨如何利用Java的正则表达式功能来提取、过滤和操作日志数据。 首先,我们需要理解正则表达式的基本概念。正则表达式(Regular Expression)是一种模式...
7. **日志格式和内容**:为了便于后期分析,日志应包含足够的上下文信息,如线程ID、时间戳、调用堆栈等。这可以帮助开发者快速定位问题所在。 8. **分布式日志系统**:在大型分布式系统中,可能需要使用分布式日志...
# 使用jstack查看指定进程ID的线程堆栈信息 jstack ``` 为了定期收集这些信息,我们可以编写一个名为`collectjstack.sh`的Shell脚本。这个脚本可以设置为定时任务,如cron job,以定期记录Java进程的状态,这对于...
- **格式化输出**:提供统一的日志格式,包括时间戳、日志级别、线程信息、日志内容等,便于阅读和分析。 - **异常堆栈追踪**:当记录错误时,自动捕获并打印异常的堆栈信息,有助于定位问题所在。 - **文件输出*...
因此,Java堆栈记录了程序运行时的调用层次结构,这对于调试和性能分析具有重要意义。 ### 打印Java堆栈的重要性 打印Java堆栈可以帮助开发者了解程序的执行流程,特别是在遇到异常或性能瓶颈时,通过查看堆栈信息...
Java Core文件是由JVM生成的一种日志文件,记录了JVM在特定时刻的状态,包括线程堆栈信息、内存使用情况、类加载信息等。当Java应用程序出现异常或者性能问题时,操作系统可能会触发JVM生成javacore文件,帮助...
同时,还要检查堆栈跟踪,确定哪些线程或方法在崩溃时刻正在执行,并结合Java堆、方法区、元数据等内存区域的状态进行分析。 总之,理解和分析JVM崩溃日志是诊断和解决Java应用程序性能问题的关键步骤。通过深入...
2. **日志收集**:通过ELK(Elasticsearch、Logstash、Kibana)堆栈或Splunk等工具收集分散的日志,便于集中管理和分析。 3. **日志安全**:保护敏感信息不被泄露,如密码、用户信息等,可以通过日志过滤器实现。 4....
然后,使用分析工具打开这个文件,查看线程堆栈、内存分配、类加载等信息。通过这些信息,你可以定位到导致问题的具体代码段,进而进行优化或修复。 总之,JavaCore分析工具是Java开发和运维过程中不可或缺的诊断...
6. **日志格式化**:可以定制日志输出的格式,包括时间戳、线程信息、类名、方法名、堆栈跟踪等,方便日志分析。 7. **日志过滤**:可以通过配置过滤特定的日志信息,避免无关或者敏感信息的泄露。 8. **日志归档...
1. Java线程堆栈分析 - 内存泄露定位:内存泄露指的是程序在分配了内存之后,由于某些原因未能释放已不再使用的内存,导致内存持续消耗而无剩余。定位内存泄露通常需要分析内存使用情况,识别出那些不再被程序使用...
### Java线上故障分析:线程dump与堆内存分析 #### 引言 在现代软件开发中,Java作为一门广泛使用的编程语言,在企业级应用、Web服务、大数据处理等多个领域发挥着重要作用。然而,随着系统复杂度的提升,线上环境...