Java内存泄漏分析系列之五:常见的Thread Dump日志案例分析

时间:2022-09-05 08:05:07

原文地址:http://www.javatang.com

症状及解决方案

下面列出几种常见的症状即对应的解决方案:

CPU占用率很高,响应很慢

按照《Java内存泄漏分析系列之一:使用jstack定位线程堆栈信息》中所说的方法,先找到占用CPU的进程,然后再定位到对应的线程,最后分析出对应的堆栈信息。
在同一时间多次使用上述的方法,然后进行对比分析,从代码中找到问题所在的原因。如果线程指向的是"VM Thread"或者无法从代码中直接找到原因,就需要进行内存分析,具体的见下一篇文章。

CPU占用率不高,但响应很慢

在整个请求的过程中多次执行Thread Dump然后进行对比,取得 BLOCKED 状态的线程列表,通常是因为线程停在了I/O、数据库连接或网络连接的地方。

关注点概况

在Thread Dump文件中,线程的状态分成两种:Native Thread StatusJVM Thread Status,具体的含义可以参考上一篇文章。在分析日志的时候需要重点关注如下几种线程状态:

系统线程状态为 deadlock

线程处于死锁状态,将占用系统大量资源。

系统线程状态为 waiting for monitor entry 或 in Object.wait()

如上一篇文章中所说,系统线程处于这种状态说明它在等待进入一个临界区,此时JVM线程的状态通常都是 java.lang.Thread.State: BLOCKED。

如果大量线程处于这种状态的话,可能是一个全局锁阻塞了大量线程。如果短期内多次打印Thread Dump信息,发现 waiting for monitor entry 状态的线程越来越多,没有减少的趋势,可能意味着某些线程在临界区里呆得时间太长了,以至于越来越多新线程迟迟无法进入。

系统线程状态为 waiting on condition

系统线程处于此种状态说明它在等待另一个条件的发生来唤醒自己,或者自己调用了sleep()方法。此时JVM线程的状态通常是java.lang.Thread.State: WAITING (parking)(等待唤醒条件)或java.lang.Thread.State: TIMED_WAITING (parking或sleeping)(等待定时唤醒条件)。

如果大量线程处于此种状态,说明这些线程又去获取第三方资源了,比如第三方的网络资源或读取数据库的操作,长时间无法获得响应,导致大量线程进入等待状态。因此,这说明系统处于一个网络瓶颈或读取数据库操作时间太长。

系统线程状态为 blocked

线程处于阻塞状态,需要根据实际情况进行判断。

案例分析

下面通过几个案例进行分解来获得解决问题的方法。

waiting for monitor entry 和 java.lang.Thread.State: 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)

上面系统线程的状态是 waiting for monitor entry,说明此线程通过 synchronized(obj) { } 申请进入临界区,但obj对应的 Monitor 被其他线程所拥有,所以 JVM线程的状态是 java.lang.Thread.State: BLOCKED (on object monitor),说明线程等待资源超时。

下面的 waiting to lock <0xe0375410> 说明线程在等待给 0xe0375410 这个地址上锁(trying to obtain 0xe0375410 lock),如果在日志中发现有大量的线程都在等待给 0xe0375410 上锁的话,这个时候需要在日志中查找那个线程获取了这个锁 locked <0xe0375410>,如上面的例子中是 "DB-Processor-14" 这个线程,这样就可以顺藤摸瓜了。上面的例子是因为获取数据库操作等待的时间太长所致的,这个时候就需要修改数据库连接的配置信息。

如果两个线程相互都被对方的线程锁锁住,这样就造成了 死锁 现象,如下面的例子所示:
Java内存泄漏分析系列之五:常见的Thread Dump日志案例分析

waiting on condition 和 java.lang.Thread.State: TIMED_WAITING

"RMI TCP Connection(idle)" daemon prio=10 tid=0x00007fd50834e800 nid=0x56b2 waiting on condition [0x00007fd4f1a59000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000acd84de8> (a java.util.concurrent.SynchronousQueue$TransferStack)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:198)
at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:424)
at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:323)
at java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:874)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:945)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
at java.lang.Thread.run(Thread.java:662)

JVM线程的状态是 java.lang.Thread.State: TIMED_WAITING (parking),说明线程处于定时等待的状态,parking指线程处于挂起中。

waiting on condition需要结合堆栈中的 parking to wait for <0x00000000acd84de8> (a java.util.concurrent.SynchronousQueue$TransferStack) 一起来分析。首先,本线程肯定是在等待某个条件的发生来把自己唤醒。其次,SynchronousQueue并不是一个队列,只是线程之间移交信息的机制,当我们把一个元素放入到 SynchronousQueue 中的时候必须有另一个线程正在等待接受移交的任务,因此这就是本线程在等待的条件。

in Object.wait() 和 java.lang.Thread.State: TIMED_WAITING

"RMI RenewClean-[172.16.5.19:28475]" daemon prio=10 tid=0x0000000041428800 nid=0xb09 in Object.wait() [0x00007f34f4bd0000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000aa672478> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118)
- locked <0x00000000aa672478> (a java.lang.ref.ReferenceQueue$Lock)
at sun.rmi.transport.DGCClient$EndpointEntry$RenewCleanThread.run(DGCClient.java:516)
at java.lang.Thread.run(Thread.java:662)

本例中JVM线程的状态是 java.lang.Thread.State: TIMED_WAITING (on object monitor),说明线程调用了 java.lang.Object.wait(long timeout) 方法而进入了等待状态。

"Wait Set"中等待的线程状态就是 in Object.wait(),当线程获得了 Monitor进入临界区之后,如果发现线程继续运行的条件没有满足,它就调用对象(通常是被 synchronized 的对象)的wait()方法,放弃了Monitor,进入 "Wait Set" 队列中。只有当别的线程在该对象上调用了 notify()或notifyAll()方法, "Wait Set" 队列中线程才得到机会去竞争,但是只有一个线程获得对象的 Monitor,恢复到的运行态。

另外需要注意的是,是先 locked <0x00000000aa672478> 然后再 waiting on <0x00000000aa672478>,之所以如此,可以通过下面的代码进行演示:

static private class  Lock { };
private Lock lock = new Lock();
public Reference<? extends T> remove(long timeout) {
synchronized (lock) {
Reference<? extends T> r = reallyPoll();
if (r != null) return r;
for (;;) {
lock.wait(timeout);
r = reallyPoll();
// ……
}
}

线程在执行的过程中,先用 synchronized 获得了这个对象的 Monitor(对应 locked <0x00000000aa672478>),当执行到 lock.wait(timeout); 的时候,线程就放弃了Monitor的所有权,进入 "Wait Set" 队列(对应 waiting on <0x00000000aa672478>)。

前面几篇文章详细说明了如何分析Thread Dump文件,除此之外还可以通过分析JVM堆内存信息来进一步找到问题的原因。

Java内存泄漏分析系列之五:常见的Thread Dump日志案例分析的更多相关文章

  1. jstack生成的Thread Dump日志线程 分析

    文章转载自: https://www.javatang.com/archives/2017/10/25/36441958.html 前面文章中只分析了Thread Dump日志文件的结构,今天针对日志 ...

  2. Java内存泄漏分析系列之四:jstack生成的Thread Dump日志线程状态

    原文地址:http://www.javatang.com Thread Dump日志的线程信息 以下面的日志为例: "resin-22129" daemon prio=10 tid ...

  3. JVM故障分析系列之四:jstack生成的Thread Dump日志线程状态

    JVM故障分析系列之四:jstack生成的Thread Dump日志线程状态  2017年10月25日  Jet Ma  JavaPlatform JVM故障分析系列系列文章 JVM故障分析系列之一: ...

  4. Java内存泄漏分析系列之二:jstack生成的Thread Dump日志结构解析

    原文地址:http://www.javatang.com 一个典型的thread dump文件主要由一下几个部分组成: 上图将JVM上的线程堆栈信息和线程信息做了详细的拆解. 第一部分:Full th ...

  5. Java内存泄漏分析系列之一:使用jstack定位线程堆栈信息

    原文地址:http://www.javatang.com 前一段时间上线的系统升级之后,出现了严重的高CPU的问题,于是开始了一系列的优化处理之中,现在将这个过程做成一个系列的文章. 基本概念 在对J ...

  6. java内存泄漏的定位与分析

    1.为什么会发生内存泄漏 java 如何检测内在泄漏呢?我们需要一些工具进行检测,并发现内存泄漏问题,不然很容易发生down机问题. 编写java程序最为方便的地方就是我们不需要管理内存的分配和释放, ...

  7. Java内存泄漏分析与解决方案

    Java内存泄漏是每个Java程序员都会遇到的问题,程序在本地运行一切正常,可是布署到远端就会出现内存无限制的增长,最后系统瘫痪,那么如何最快最好的检测程序的稳定性,防止系统崩盘,作者用自已的亲身经历 ...

  8. (转)java内存泄漏的定位与分析

    转自:http://blog.csdn.net/x_i_y_u_e/article/details/51137492 1.为什么会发生内存泄漏 java 如何检测内在泄漏呢?我们需要一些工具进行检测, ...

  9. Java内存泄漏分析和预防

    1. 什么是内存泄漏?有什么危害 书面说法: 内存泄漏:对象已经没有被应用程序使用,但是垃圾回收器没办法移除它们,因为还在被引用着. 在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个 ...

随机推荐

  1. Python 2x -&gt&semi; 3&period;x

    Nowadays, Python 3 is becoming more and more popular than Python 2, but there are still a lot of cod ...

  2. Java递归算法——变位字

    轮换的含义 1.c ats --> 2.ca st 3.c tsa --> 4.ct as 5.c sat --> 6.cs ta 7. atsc import java.io.Bu ...

  3. web设计经验&lt&semi;一&gt&semi; 提升移动设备响应式设计的8个建议

    今天看到一些关于web设计的一些建议和设计经验,拿出来分享分享. 第一篇: 提升移动设备响应式设计的8个建议 一.直观性和易用性 在使用移动设备时,对于杂乱.复杂或者不直观的设计造成的混乱不佳的用户体 ...

  4. PL&sol;SQL Developer使用

    查询存储过程方法:1.右上角小百页 - 新建SQL窗口 - 复制存储过程名称 - 按住CTRL - 点击链接进入2.点击查询按钮(望远镜) - 文本查找输入名称 - 对象类型默认(函数.过程,包说明, ...

  5. 教你如何利用xml格式的sitemap文件做好SEO

    教你如何利用xml格式的sitemap文件做好SEO 浏览: | 更新:-- : 一般的网站中都有网站地图文件,它有HTML格式与XML格式,网站地图可以帮助搜索引擎抓取.帮助用户找到自己所需要的内容 ...

  6. 说说BroadcastReceiver和ContentProvider

    上一篇说了Activity,Fragment和Service,今天来说说四大组件中的另外两个吧. BroadcastReceiver: 广播在实际开发中非常有用,是各个组件间通讯的利器.广播接收器分为 ...

  7. vue-cli脚手架npm相关文件解读(8)check-versions&period;js

    系列文章传送门: 1.build/webpack.base.conf.js 2.build/webpack.prod.conf.js 3.build/webpack.dev.conf.js 4.bui ...

  8. 浅谈我的MongoDB学习(一)

    这是第一次写博客,不当之处敬请见谅,最近由于项目需要,对mongodb略有研究,网上也有一些相关资料,下面是我自己摸索的一些东西,希望能跟大家分享一下当然,这也是我自己第一次在项目中使用,若理解有误, ...

  9. CentOS7搭建LAMP实战

    环境配置从官网下载稳定的源码包解压预编译编译编译安装启动服务 环境配置 # yum install -y vim wget links //安装一下基本工具# systemctl stop firew ...

  10. MVC EF 移除建表时自动加上s的复数形式

    移除建表时自动加上s的复数形式 public class DBContext : DbContext { public DBContext() : base("name=DBContext& ...