使用top +jps+jstack定位cpu占用100%的Java服务问题定位简易操作指引

1. 使用top命令找出CPU占用最多的应用

首先,你需要使用top命令来识别哪个进程正在使用大量的CPU资源。

  • 运行top命令:
  • 在终端中输入top并按下回车键。
  • 查看CPU使用率最高的进程:
  • 默认情况下,top会按CPU使用率排序。查看%CPU列,找出使用率最高的进程。
  • 识别Java进程:
  • 如果发现CPU使用率最高的是Java进程(在COMMAND列显示为java),记下它的PID。

2. 使用jpsjstack获取Java进程的线程堆栈跟踪

接下来,使用jps命令来确认Java进程的ID,然后用jstack来获取线程的堆栈跟踪。

  • 使用jps定位Java进程:
    • 运行jps -l以列出所有Java进程及其主类的全限定名。这有助于确认你在top中看到的Java进程。
  • 使用jstack获取线程堆栈:
    • 对于每个可疑的Java进程,使用jstack加进程ID来获取线程堆栈跟踪:jstack 。这将输出所有线程的当前调用堆栈。

3. 分析堆栈跟踪

通过分析jstack输出的堆栈跟踪,你可以尝试找到CPU使用率高的线程。这里有些指标可以帮助你:

  • 查找状态为RUNNABLE的线程,因为它们是实际在CPU上运行的线程。
  • 检查这些线程的堆栈跟踪,寻找正在执行的方法和代码行。
  • 注意看是否有特定的方法或循环被频繁调用,这可能是CPU使用率高的原因。

示例分析

假设你运行了jstack,得到了类似以下的输出片段:

"Thread-1" #10 prio=5 os_prio=0 tid=0x00007f2a5400e800 nid=0x6e03 runnable [0x00007f2a4d9fa000]
   java.lang.Thread.State: RUNNABLE
    at MyAppPackage.MyBusyClass.busyMethod(MyBusyClass.java:50)
    at MyAppPackage.MyBusyClass.run(MyBusyClass.java:35)
    at java.lang.Thread.run(Thread.java:748)

这是一个示例堆栈跟踪,其中关键信息包括:

  • 线程状态 (java.lang.Thread.State): RUNNABLE表明该线程正在Java虚拟机中执行,是一个活跃运行的线程。
  • 执行的方法和代码行: MyBusyClass.busyMethodMyBusyClass.java:50。这是线程正在执行的方法,如果这行代码或方法在多个堆栈跟踪中反复出现,可能表明它是CPU使用率高的原因。

通过查找此类模式,你可以识别出可能导致CPU使用率高的线程和代码段。

4. 进行代码级分析

一旦你发现了可能导致高CPU使用的线程和方法,你就需要查看相应的源代码。在代码中寻找如下问题:

  • 无限循环或高效率循环。
  • 性能低下的算法。
  • 大量的同步操作或死锁。
  • 不必要的资源密集型操作。

5. 修改代码

===============================================================

附录:

Top命令 主要输出指标的解释:

  • PID: 进程标识符,用于唯一标识系统中的每个进程。
  • COMMAND: 正在执行的命令的名称,这里是java。
  • %CPU: 此进程使用的CPU百分比。
  • TIME: 此进程累积占用的CPU时间。
  • #TH: 进程使用的线程数
  • #WQ: 在等待队列中的线程数量。
  • #POR: 进程使用的端口数量。
  • MEM: 进程占用的物理内存大小
  • PURG: 被清理的内存大小。
  • CMPR: 压缩的内存大小。
  • PGRP: 进程组ID。
  • PPID: 父进程的PID
  • STATE: 进程的当前状态。
  • BOOSTS: 任何优先级提升(或降低)的标记。
  • %CPU_ME: 此进程自身的CPU使用率。
  • %CPU_OTHRS: 其他进程的CPU使用率。
  • UID: 用户标识符,标识运行此进程的用户
  • FAULT: 页面错误的次数。
  • COW: 写时复制的页面数。
  • MSGSEN/MSGREC: 发送和接收的消息数。
  • SYSBSD/SYSMACH: BSD和Mach系统调用的次数。
  • CSW: 上下文切换次数。
  • PAGE: 页面错误的次数。
  • IDLE: CPU空闲时间的百分比。
  • POWE: 能耗估算。
  • INSTRS: 执行的指令数。
  • CYCLES: 处理器周期数。
  • USER: 运行此进程的用户名。

通过一个简化的 jstack 输出例子来解释其中的一些关键指标。这里是一个典型的 jstack 输出示例:

"main" #1 prio=5 os_prio=0 tid=0x00007f0f74008000 nid=0x1e15 waiting on condition [0x00007f0f74efb000] java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep(Native Method) - locked <0x00000000f5a0f5b8> (a java.lang.ref.Reference$Lock) at com.example.MyClass.myMethod(MyClass.java:123)

现在,让我们解析这个输出:

  • 线程名称和序号
    • "main" #1:这是线程的名称和序号。这里的线程名是 "main",通常是 Java 程序的主线程。#1 表示这是 JVM 中的第一个线程。
  • 线程优先级
    • prio=5:这是线程的优先级,范围从 1(最低)到 10(最高)。这里的优先级是 5,是默认值。
  • 操作系统线程优先级
    • os_prio=0:这是线程在操作系统层面的优先级。这个值依赖于操作系统和 JVM 的实现。
  • 线程 ID
    • tid=0x00007f0f74008000:这是 JVM 内部为线程分配的唯一标识符,以十六进制表示。
  • 本地线程 ID
    • nid=0x1e15:这是操作系统为线程分配的本地标识符,同样以十六进制表示。在某些情况下,可用于将 JVM 中的线程与操作系统级别的线程关联起来。
  • 线程状态
    • java.lang.Thread.State: TIMED_WAITING (sleeping):这是线程的状态。TIMED_WAITING 通常意味着线程正在执行某种形式的等待,这里是由于 sleep 方法引起的。
  • 方法调用
    • at java.lang.Thread.sleep(Native Method):这显示了线程在调用栈的当前位置。在这个例子中,线程正在执行 Thread.sleep 方法。
    • at com.example.MyClass.myMethod(MyClass.java:123):这是调用栈的下一个记录。这表明 myMethod 在 MyClass.java 的第 123 行被调用。
  • 锁信息
    • - locked <0x00000000f5a0f5b8>:这表示线程当前持有的锁。锁是在对象 0x00000000f5a0f5b8 上,这是一个内部的对象引用。

通过分析这些信息,可以获取线程的当前状态和活动,这对于诊断性能问题和并发问题非常有帮助。例如,如果多个线程都在等待同一个锁,那可能就是死锁的迹象。或者,如果一个线程长时间处于 RUNNABLE 状态并消耗大量 CPU,那可能是性能问题的源头。

你可能感兴趣的:(java,开发语言)