假设你的Java程序占用了100%的CPU。它有50个线程。你需要找到哪个线程是有罪的。我还没有找到可以提供帮助的工具。目前我使用以下非常耗时的例行程序:
jstack <pid>
,其中pid是Java进程的进程ID。找到它的简单方法是运行JDK中包含的另一个实用程序 - jps
。最好将jstack的输出重定向到文件。或者,您可以附加到Eclipse中的Java进程并尝试逐个挂起线程,直到遇到占用CPU的那个。在单CPU机器上,您可能需要首先降低Java进程的优先级才能移动。即便如此,由于超时,Eclipse通常无法附加到正在运行的进程。
我原本期望Sun的visualvm
工具可以做到这一点。
有人知道更好的方法吗?
答案 0 :(得分:72)
确定哪个Java线程占用了生产服务器中的大多数CPU。
大多数(如果不是全部)生产系统执行任何重要操作都会使用多个java线程。当一些事情变得疯狂并且你的cpu使用率达到100%时,很难确定导致这种情况的线程。或者我想。直到比我聪明的人向我展示如何做到这一点。在这里,我将告诉你如何做到这一点,你也可以用你的极客技能让你的家人和朋友惊叹。
测试应用
为了测试这个,我们需要一个测试应用程序。所以我会给你一个。它由3个类组成:
HeavyThread
类(计算MD5哈希值)LightThread
类,它执行的操作不是那么密集(计数和休眠)。StartThreads
类,用于启动1个cpu密集型和几个轻型线程。以下是这些类的代码:
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
/**
* thread that does some heavy lifting
*
* @author srasul
*
*/
public class HeavyThread implements Runnable {
private long length;
public HeavyThread(long length) {
this.length = length;
new Thread(this).start();
}
@Override
public void run() {
while (true) {
String data = "";
// make some stuff up
for (int i = 0; i < length; i++) {
data += UUID.randomUUID().toString();
}
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
// hash the data
digest.update(data.getBytes());
}
}
}
import java.util.Random;
/**
* thread that does little work. just count & sleep
*
* @author srasul
*
*/
public class LightThread implements Runnable {
public LightThread() {
new Thread(this).start();
}
@Override
public void run() {
Long l = 0l;
while(true) {
l++;
try {
Thread.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
if(l == Long.MAX_VALUE) {
l = 0l;
}
}
}
}
/**
* start it all
*
* @author srasul
*
*/
public class StartThreads {
public static void main(String[] args) {
// lets start 1 heavy ...
new HeavyThread(1000);
// ... and 3 light threads
new LightThread();
new LightThread();
new LightThread();
}
}
假设您从未见过这段代码,并且您拥有一个运行这些类且耗尽100%CPU的失控Java进程的PID。
首先让我们开始StartThreads
课程。
$ ls
HeavyThread.java LightThread.java StartThreads.java
$ javac *
$ java StartThreads &
在这个阶段,一个正在运行的Java进程应该占用100个cpu。在我的顶部,我看到:
在顶部按Shift-H打开线程。 top的手册页说:
-H : Threads toggle
Starts top with the last remembered 'H' state reversed. When
this toggle is On, all individual threads will be displayed.
Otherwise, top displays a summation of all threads in a
process.
现在在我的顶部,Threads显示开启,我看到:
我有一个java
进程,其中包含PID 28294
。让我们使用jstack
获取此进程的堆栈转储:
$ jstack 28924
2010-11-18 13:05:41
Full thread dump Java HotSpot(TM) 64-Bit Server VM (17.0-b16 mixed mode):
"Attach Listener" daemon prio=10 tid=0x0000000040ecb000 nid=0x7150 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"DestroyJavaVM" prio=10 tid=0x00007f9a98027800 nid=0x70fd waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Thread-3" prio=10 tid=0x00007f9a98025800 nid=0x710d waiting on condition [0x00007f9a9d543000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at LightThread.run(LightThread.java:21)
at java.lang.Thread.run(Thread.java:619)
"Thread-2" prio=10 tid=0x00007f9a98023800 nid=0x710c waiting on condition [0x00007f9a9d644000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at LightThread.run(LightThread.java:21)
at java.lang.Thread.run(Thread.java:619)
"Thread-1" prio=10 tid=0x00007f9a98021800 nid=0x710b waiting on condition [0x00007f9a9d745000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at LightThread.run(LightThread.java:21)
at java.lang.Thread.run(Thread.java:619)
"Thread-0" prio=10 tid=0x00007f9a98020000 nid=0x710a runnable [0x00007f9a9d846000]
java.lang.Thread.State: RUNNABLE
at sun.security.provider.DigestBase.engineReset(DigestBase.java:139)
at sun.security.provider.DigestBase.engineUpdate(DigestBase.java:104)
at java.security.MessageDigest$Delegate.engineUpdate(MessageDigest.java:538)
at java.security.MessageDigest.update(MessageDigest.java:293)
at sun.security.provider.SecureRandom.engineNextBytes(SecureRandom.java:197)
- locked <0x00007f9aa457e400> (a sun.security.provider.SecureRandom)
at sun.security.provider.NativePRNG$RandomIO.implNextBytes(NativePRNG.java:257)
- locked <0x00007f9aa457e708> (a java.lang.Object)
at sun.security.provider.NativePRNG$RandomIO.access$200(NativePRNG.java:108)
at sun.security.provider.NativePRNG.engineNextBytes(NativePRNG.java:97)
at java.security.SecureRandom.nextBytes(SecureRandom.java:433)
- locked <0x00007f9aa4582fc8> (a java.security.SecureRandom)
at java.util.UUID.randomUUID(UUID.java:162)
at HeavyThread.run(HeavyThread.java:27)
at java.lang.Thread.run(Thread.java:619)
"Low Memory Detector" daemon prio=10 tid=0x00007f9a98006800 nid=0x7108 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"CompilerThread1" daemon prio=10 tid=0x00007f9a98004000 nid=0x7107 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"CompilerThread0" daemon prio=10 tid=0x00007f9a98001000 nid=0x7106 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" daemon prio=10 tid=0x0000000040de4000 nid=0x7105 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" daemon prio=10 tid=0x0000000040dc4800 nid=0x7104 in Object.wait() [0x00007f9a97ffe000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00007f9aa45506b0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118)
- locked <0x00007f9aa45506b0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:134)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:159)
"Reference Handler" daemon prio=10 tid=0x0000000040dbd000 nid=0x7103 in Object.wait() [0x00007f9a9de92000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00007f9aa4550318> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:485)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116)
- locked <0x00007f9aa4550318> (a java.lang.ref.Reference$Lock)
"VM Thread" prio=10 tid=0x0000000040db8800 nid=0x7102 runnable
"GC task thread#0 (ParallelGC)" prio=10 tid=0x0000000040d6e800 nid=0x70fe runnable
"GC task thread#1 (ParallelGC)" prio=10 tid=0x0000000040d70800 nid=0x70ff runnable
"GC task thread#2 (ParallelGC)" prio=10 tid=0x0000000040d72000 nid=0x7100 runnable
"GC task thread#3 (ParallelGC)" prio=10 tid=0x0000000040d74000 nid=0x7101 runnable
"VM Periodic Task Thread" prio=10 tid=0x00007f9a98011800 nid=0x7109 waiting on condition
JNI global references: 910
从我的顶部,我看到顶线程的PID是28938
。十六进制中的28938
为0x710A
。请注意,在堆栈转储中,每个线程都有一个nid
,它以十六进制形式显示。恰好,0x710A
是线程的id:
"Thread-0" prio=10 tid=0x00007f9a98020000 nid=0x710a runnable [0x00007f9a9d846000]
java.lang.Thread.State: RUNNABLE
at sun.security.provider.DigestBase.engineReset(DigestBase.java:139)
at sun.security.provider.DigestBase.engineUpdate(DigestBase.java:104)
at java.security.MessageDigest$Delegate.engineUpdate(MessageDigest.java:538)
at java.security.MessageDigest.update(MessageDigest.java:293)
at sun.security.provider.SecureRandom.engineNextBytes(SecureRandom.java:197)
- locked <0x00007f9aa457e400> (a sun.security.provider.SecureRandom)
at sun.security.provider.NativePRNG$RandomIO.implNextBytes(NativePRNG.java:257)
- locked <0x00007f9aa457e708> (a java.lang.Object)
at sun.security.provider.NativePRNG$RandomIO.access$200(NativePRNG.java:108)
at sun.security.provider.NativePRNG.engineNextBytes(NativePRNG.java:97)
at java.security.SecureRandom.nextBytes(SecureRandom.java:433)
- locked <0x00007f9aa4582fc8> (a java.security.SecureRandom)
at java.util.UUID.randomUUID(UUID.java:162)
at HeavyThread.run(HeavyThread.java:27)
at java.lang.Thread.run(Thread.java:619)
因此您可以确认运行HeavyThread
类的线程消耗的CPU最多。
在读取世界的情况下,它可能是一堆线程占用CPU的一部分,这些线程放在一起将导致使用100%CPU的Java进程。
摘要
答案 1 :(得分:18)
jvmtop可以向您显示消费最多的主题:
TID NAME STATE CPU TOTALCPU
25 http-8080-Processor13 RUNNABLE 4.55% 1.60%
128022 RMI TCP Connection(18)-10.101. RUNNABLE 1.82% 0.02%
36578 http-8080-Processor164 RUNNABLE 0.91% 2.35%
128026 JMX server connection timeout TIMED_WAITING 0.00% 0.00%
答案 2 :(得分:15)
尝试查看Hot Thread Detector plugin for visual VM - 它使用ThreadMXBean API获取多个CPU消耗样本以查找最活跃的线程。它基于a command-line equivalent from Bruce Chapman,也可能有用。
答案 3 :(得分:10)
只需运行JVisualVM,连接到您的应用程序并使用线程视图。持续保持活跃的是你最可能的罪魁祸首。
答案 4 :(得分:6)
查看JConsole的Top Threads插件。
答案 5 :(得分:2)
如果您在Windows下运行,请尝试Process Explorer。打开流程的属性对话框,然后选择“线程”选项卡。
答案 6 :(得分:1)
进行线程转储。等待10秒钟。另一个线程转储。再重复一次。 检查线程转储并查看哪些线程卡在同一位置,或处理相同的请求。 这是一种手动方式,但通常很有用。
答案 7 :(得分:1)
答案 8 :(得分:1)
我建议您看看由阿里巴巴开源的Arthas工具。
它包含一堆有用的命令,可以帮助您调试生产代码:
答案 9 :(得分:0)
这是一种hacky方式,但似乎你可以在调试器中启动应用程序,然后暂停所有线程,并查看代码并找出哪个不是在某种循环中阻塞锁或I / O调用。或者这就像你已经尝试过的那样?
答案 10 :(得分:0)
您可以考虑的一个选项是从应用程序内部查询线程以获得答案。通过ThreadMXBean,您可以从Java应用程序中查询线程的CPU使用情况,并查询有问题的线程的堆栈跟踪。
ThreadMXBean选项允许您在实时应用程序中构建此类监视。它的影响可以忽略不计,并且具有明显的优势,您可以使其完全符合您的要求。
答案 11 :(得分:0)
如果您怀疑VisualVM是一个很好的工具,请尝试它(因为它会这样做)找出线程只能帮助您解决为什么它消耗这么多CPU的一般方向。
然而,如果明显我会直接使用分析器来找出你消耗这么多CPU的原因。
答案 12 :(得分:0)
使用ps -eL
或top -H -p <pid>
,或者如果需要实时查看和监视,请运行top(然后移H)以获取与以下内容相关联的轻量级过程(LWP aka线程) Java进程。
root@xxx:/# ps -eL
PID LWP TTY TIME CMD
1 1 ? 00:00:00 java
1 7 ? 00:00:01 java
1 8 ? 00:07:52 java
1 9 ? 00:07:52 java
1 10 ? 00:07:51 java
1 11 ? 00:07:52 java
1 12 ? 00:07:52 java
1 13 ? 00:07:51 java
1 14 ? 00:07:51 java
1 15 ? 00:07:53 java
…
1 164 ? 00:00:02 java
1 166 ? 00:00:02 java
1 169 ? 00:00:02 java
Note LWP =轻量级进程;在Linux中,线程与进程相关联,因此可以在内核中进行管理。 LWP与父进程共享文件和其他资源。 现在,让我们看看花费最多时间的线程
1 8 ? 00:07:52 java
1 9 ? 00:07:52 java
1 10 ? 00:07:51 java
1 11 ? 00:07:52 java
1 12 ? 00:07:52 java
1 13 ? 00:07:51 java
1 14 ? 00:07:51 java
1 15 ? 00:07:53 java
Jstack 是用于打印Java Stack的JDK实用程序;它打印表单的线程。
也熟悉其他很棒的JDK工具( jcmd jstat jhat jmap jstack 等-https://docs.oracle.com/javase/8/docs/technotes/tools/unix/)
jstack -l <process id>
堆栈跟踪中的第一个本地线程id是在Linux(https://gist.github.com/rednaxelafx/843622)中连接到LWT的线程。
“GC task thread#0 (ParallelGC)” os_prio=0 tid=0x00007fc21801f000 nid=0x8 runnable
nid以十六进制表示;因此,我们将花费最多时间转换线程ID DEC中的8,9,10,11,12,13,14,15 =十六进制中的8,9,A,B,C,D,E,F。
(请注意,该特定堆栈是从Docker容器中的Java提取的,如果为1,则具有便利的过程) 让我们来看一下具有此ID的线程。
“GC task thread#0 (ParallelGC)” os_prio=0 tid=0x00007fc21801f000 nid=0x8 runnable
“GC task thread#1 (ParallelGC)” os_prio=0 tid=0x00007fc218020800 nid=0x9 runnable
“GC task thread#2 (ParallelGC)” os_prio=0 tid=0x00007fc218022800 nid=0xa runnable
“GC task thread#3 (ParallelGC)” os_prio=0 tid=0x00007fc218024000 nid=0xb runnable
“GC task thread#4 (ParallelGC)” os_prio=0 tid=0x00007fc218026000 nid=0xc runnable
“GC task thread#5 (ParallelGC)” os_prio=0 tid=0x00007fc218027800 nid=0xd runnable
“GC task thread#6 (ParallelGC)” os_prio=0 tid=0x00007fc218029800 nid=0xe runnable
“GC task thread#7 (ParallelGC)” os_prio=0 tid=0x00007fc21802b000 nid=0xf runnable
所有与GC相关的线程;难怪这会占用大量的CPU时间。但是GC在这里是一个问题。
使用jstat(不是jstack!)实用工具快速检查GC。
jstat -gcutil <pid>