JVM 线程分析

使用

导出线程堆栈:jstack  pid > c:/file.txt

线程状态

Java官方定义的线程状态有6种,定义在一个枚举类中:

NEW,未启动的线程,不会出现在Dump中;

RUNNABLE,运行中,包括就绪状态和运行中状态, 调用start方法并获取到锁后进入就绪状态,获取到CPU后进入运行中状态;(当线程遇到I/O或者调用suspend挂起线程时还是在运行状态);

BLOCKED,受阻塞并等待监视器锁,被某个锁(synchronizers)給block住了;

WATING,无限期等待某个condition或monitor发生,一般停留在park, wait, sleep,join (未设置超时时间)等语句里;

TIMED_WATING,有时限的等待另一个线程的特定操作,如sleep,加上超时时间的wait;

TERMINATED,已退出的。

堆栈信息中不存在NEW和TERMINATED状态。

监视器(Monitor)

Monitor是用以实现线程之间的互斥与协作的主要手段,它可以看成是对象或者 Class的锁。每一个对象都有,也仅有一个 monitor。

Monitor可以类比为一个特殊的房间,这个房间中有一些被保护的数据,Monitor保证每次只能有一个线程能进入这个房间进行访问被保护的数据,进入房间即为持有Monitor,退出房间即为释放Monitor。

当一个线程需要访问受保护的数据(即需要获取对象的Monitor)时,它会首先在entry-set入口队列中排队(这里并不是真正的按照排队顺序),如果没有其他线程正在持有对象的Monitor,那么它会和entry-set队列和wait-set队列中的被唤醒的其他线程进行竞争(即通过CPU调度),选出一个线程来获取对象的Monitor,执行受保护的代码段,执行完毕后释放Monitor,如果已经有线程持有对象的Monitor,那么需要等待其释放Monitor后再进行竞争。

再说一下wait-set队列。当一个线程拥有Monitor后,经过某些条件的判断(比如用户取钱发现账户没钱),这个时候需要调用Object的wait方法,线程就释放了Monitor,进入wait-set队列,等待Object的notify方法(比如用户向账户里面存钱)。当该对象调用了notify方法或者notifyAll方法后,wait-set中的线程就会被唤醒,然后在wait-set队列中被唤醒的线程和entry-set队列中的线程一起通过CPU调度来竞争对象的Monitor,最终只有一个线程能获取对象的Monitor。


进入区(Entry Set):表示线程通过synchronized要求获取对象的锁。如果对象未被锁住,则进入拥有者,否则在进入区等待;

拥有者(The Owner):表示某一线程成功竞争到对象锁;

等待区(Wait Set):表示线程通过对象的wait方法,释放对象的锁,并在等待区等待被唤醒;


一个 Monitor在某个时刻,只能被一个线程拥有,该线程就是“Active Thread”,而其它线程都是“Waiting Thread”,分别在两个队列“ Entry Set”和“Wait Set”里面等候;

在“Entry Set”中等待的线程状态是“Waiting for monitor entry”,而在“Wait Set”中等待的线程状态是“in Object.wait()”; 

我们称被 synchronized保护起来的代码段为临界区,当一个线程申请进入临界区时,它就进入了 “Entry Set”队列;

线程堆栈解释

线程名称:若未设置,自动命名"thread-x" 或 "线程池名称-thread-x",线程池若未设置,自动命名"pool-x",若是守护线程后面会显示“daemon”;

线程优先级:最低1,最高10,默认5,优先级高的不一定先执行;

nid:tid映射的操作系统中的线程id,这里是用16进制的表示;

线程动作(本地线程状态):线程状态产生的原因

---runnable:此时线程状态一般为RUNNABLE;

---in Object.wait():等待区等待,此时线程状态为WAITING或TIMED_WAITING;

---waiting for monitor entry:进入区等待,等待进入一个临界区,所以它在”Entry Set“队列中等待,线程状态一般是 BLOCKED;

---waiting on condition:等待区等待、被park,等待另一个条件的发生来把自己唤醒,线程状态是:WAITING(parking,一直等那个条件发生)或 TIMED_WAITING (parking或sleeping,定时的,那个条件不到来,也将定时唤醒自己);

---sleeping:休眠的线程,调用了Thread.sleep();

调用修饰:表示线程在方法调用时,额外的重要的操作

---locked <地址/锁ID> 目标:使用synchronized申请对象锁成功,监视器的拥有者;

---waiting to lock <地址/锁ID> 目标:使用synchronized申请对象锁未成功,在进入区等待;

---waiting on <地址/锁ID> 目标:使用synchronized申请对象锁成功后,释放锁在等待区等待;

---parking to wait for <地址/锁ID> 目标:park是基本的线程阻塞原语,不通过监视器在对象上阻塞,与synchronized体系不同;

尖括号中表示锁ID,这个是系统自动产生的,我们只需要知道每次打印的堆栈,同一个ID表示是同一个锁即可。

---当一个线程占有一个锁的时候,线程的堆栈中会打印--locked<0x00000000d77d50c8>

---当一个线程正在等待其它线程释放该锁,线程堆栈中会打印--waiting to lock<0x00000000d77d50c8>

---当一个线程占有一个锁,但又执行到该锁的wait()方法上,线程堆栈中首先打印locked,然后又会打印--waiting on <0x00000000d77d50c8>


问题分析

大量线程在waiting for monitor entry

可能是一个全局锁阻塞住了大量线程,随着时间流逝,waiting for monitor entry 的线程越来越多,没有减少的趋势,可能意味着某些线程在临界区里呆的时间太长了,以至于越来越多新线程迟迟无法进入临界区;

大量线程在waiting on condition

可能是它们又跑去获取第三方资源,尤其是第三方网络资源,迟迟获取不到Response,导致大量线程进入等待状态,所以如果你发现有大量的线程都处在 Wait on condition,从线程堆栈看,正等待网络读写,这可能是一个网络瓶颈的征兆,因为网络阻塞导致线程无法执行;

线程在in Object.wait()

当线程获得了 Monitor,如果发现线程继续运行的条件没有满足,它则调用对象的 wait() 方法,放弃了 Monitor,进入 “Wait Set”队列,一般都是RMI相关线程(RMI RenewClean、 GC Daemon、RMI Reaper),GC线程(Finalizer),引用对象垃圾回收线程(Reference Handler)等系统线程处于这种状态;

线程处于parking to wait for  <> (a java.util.concurrent.SynchronousQueue$TransferStack)

首先,本线程肯定是在等待某个条件的发生,来把自己唤醒。其次,SynchronousQueue 并不是一个队列,只是线程之间移交信息的机制,当我们把一个元素放入到 SynchronousQueue 中时必须有另一个线程正在等待接受移交的任务,因此这就是本线程在等待的条件。

CPU占用率很高,响应很慢

先找到占用CPU的进程,然后再定位到对应的线程,最后分析出对应的堆栈信息。

Linux环境下,使用 top -Hp pid 可以查看进程下各个线程的cpu 内存情况,使用printf “%x\n” <threadid> 可将线程ID转成16进制(即上面的nid);

Windows环境下,没有自带命令,可以使用Process Explorer工具查看;

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

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

在线分析

http://gceasy.io/

使用jca工具分析


工具界面
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容