jmap使用报错Doesn't appear to be a HotSpot VM (could not find symbol "gHotSpotVMTypes" in remote process)

报错场景

问题原因

服务器上装了jdk,按理来说jmap是自带了的,可以直接使用,根据情况来看是装了jmap但是无法正常使用,推测是版本的问题导致

解决方式

指定jdk自带的jmap工具

1. 查看当前java的环境变量

echo $JAVA_HOME

2. 配置jdk自带工具的环境变量

export PATH=$JAVA_HOME/bin:$PATH

3. 成功运行jmap命令

jmap -dump:live,format=b,file=heapdump.hprof <pid>

• jmap:用于生成堆内存快照(heap dump),分析对象的内存使用情况。适用于内存泄漏分析。
• jstack:用于生成线程堆栈信息(thread dump),帮助分析线程的运行状态、死锁等问题。适用于诊断线程问题或应用假死。

生成线程堆栈信息

当服务假死时,更常用的方法是通过 jstack 生成线程堆栈信息

jstack -l <pid> > thread_dump.txt

当使用 jstack 输出线程堆栈信息后,分析这些信息可以帮助你诊断Java应用程序的各种问题,如死锁、线程卡死、性能瓶颈等。以下是 jstack 输出的线程堆栈信息的分析步骤和常见问题的识别方法:

1. 检查线程状态

  • 每个线程在堆栈信息中都有一个状态标识,常见的线程状态包括:
    • RUNNABLE:线程正在执行。
    • BLOCKED:线程被阻塞,正在等待锁资源。
    • WAITING:线程处于等待状态,等待某个条件发生。
    • TIMED_WAITING:线程处于超时等待状态。
    • TERMINATED:线程已终止。

分析重点

  • 如果有大量线程处于 BLOCKED 状态,这可能表示存在锁争用问题。
  • 如果有大量线程处于 WAITINGTIMED_WAITING 状态,这可能表明线程在等待某个条件或资源(例如网络、数据库响应),可能导致性能瓶颈。
  • 如果绝大部分线程都处于 RUNNABLE 状态,但应用仍然无响应,可能存在CPU资源耗尽或无限循环的情况。

2. 识别死锁

  • jstack 输出中会自动检测和报告死锁情况。如果存在死锁,jstack 输出的开头会明确指出,并列出涉及死锁的线程及其堆栈信息。

示例

Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock Monitor@0x12345,
  which is held by "Thread-2"
"Thread-2":
  waiting to lock Monitor@0x54321,
  which is held by "Thread-1"

分析重点

  • 找到死锁涉及的线程,并查看它们相互等待的资源。
  • 死锁通常涉及多个线程相互持有对方需要的资源,因此要找到这些线程之间的依赖关系,并在代码中避免同时持有多个锁或使用合适的锁排序策略。

3. 分析热点线程

  • 如果某些线程占用了大量CPU资源,jstack 中这些线程通常会反复显示相同的堆栈信息,即它们长时间停留在同一行代码上。

分析方法

  • 查看 RUNNABLE 状态的线程,找到那些在堆栈中显示时间最长的线程。
  • 检查这些线程在执行哪些方法,分析这些方法是否涉及耗时操作或死循环。

示例

"main" #1 prio=5 os_prio=0 tid=0x00007f8e4800b000 nid=0x1e03 runnable [0x00007f8e4c9fd000]
   java.lang.Thread.State: RUNNABLE
      at com.example.MyClass.someMethod(MyClass.java:123)
      at com.example.MyClass.run(MyClass.java:456)

分析重点

  • 检查线程堆栈顶端的方法(如 someMethod),是否在执行复杂计算、I/O操作或无限循环。
  • 如果可能,将这些操作优化或改为异步执行。

4. 查看线程数量

  • 检查 jstack 输出中的线程数量,判断是否存在过多线程导致的资源耗尽问题。过多线程可能导致CPU资源被大量上下文切换消耗,降低整体性能。

分析方法

  • 统计不同状态的线程数量,特别是 RUNNABLEBLOCKED 状态的线程。
  • 如果线程数量异常多,检查应用程序是否创建了过多线程或是否存在线程池配置问题。

5. 分析锁竞争

  • jstack 输出中,查看线程是否被锁竞争阻塞。阻塞线程通常会显示类似于 waiting to lockwaiting on condition 的信息。

示例

"Worker-1" #12 prio=5 os_prio=0 tid=0x00007f8e4800c000 nid=0x1e10 waiting for monitor entry [0x00007f8e4d9fd000]
   java.lang.Thread.State: BLOCKED (on object monitor)
      at com.example.MyClass.synchronizedMethod(MyClass.java:78)
      - waiting to lock <0x00000000dfe1bdb0> (a java.lang.Object)
      - locked <0x00000000dfd5fdb0> (a java.lang.Object)

分析重点

  • 检查锁的争用对象,分析代码中是否有过多的同步块,或者同步块中的操作是否过于复杂。
  • 优化同步逻辑,减少锁的持有时间,或者使用更细粒度的锁或无锁数据结构。

6. 定位异常线程

  • 如果你知道某个线程导致了问题,可以在 jstack 输出中搜索该线程的名称或ID,查看该线程的堆栈信息。

示例

"MyThread-1" #15 prio=5 os_prio=0 tid=0x00007f8e4800c000 nid=0x1e15 waiting on condition [0x00007f8e4d9fd000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
      at java.lang.Thread.sleep(Native Method)
      at com.example.MyClass.delayedMethod(MyClass.java:98)

分析重点

  • 查看特定线程的堆栈信息,分析线程当前正在执行的代码。
  • 如果线程处于 WAITINGTIMED_WAITING 状态,检查它等待的条件是否能正常满足,或是否存在逻辑错误。

7. 分析网络I/O或外部资源调用

  • 如果应用程序与外部资源(如数据库、文件系统、网络服务)交互,可能存在由于外部资源未响应而导致线程卡死的情况。

分析方法

  • 检查堆栈信息中,是否有大量线程卡在I/O操作上。
  • 分析应用是否正确设置了I/O超时或重试机制,避免长期等待。

总结:

  1. 首先检查线程状态,关注 BLOCKEDWAITINGTIMED_WAITING 状态的线程。
  2. 查找死锁信息jstack 会自动检测并输出。
  3. 分析热点线程,检查 RUNNABLE 状态的线程是否存在性能问题。
  4. 分析锁竞争和线程数量,避免过多的锁争用和线程上下文切换。
  5. 定位异常线程,从特定线程的堆栈信息入手,排查问题根源。

通过这些步骤,你可以有效地分析 jstack 的输出结果,定位Java应用中的假死、性能瓶颈或其他问题。

posted @ 2024-05-06 14:50  惊叫唤  阅读(423)  评论(0编辑  收藏  举报