本文目录一览:
- 1、jstack的使用
- 2、通过top和jstack确定哪些线程耗尽了CPU?这些线程在做什么
- 3、CPU占用过高,jstack导出后没看出有什么问题,求教
- 4、记一次线上机器CPU飙高的排查过程
- 5、JVM调优jstack怎么找出最耗cpu的线程并定位代码
jstack的使用
通过jstack,我们可以轻松得知jvm中各个线程的工作情况.
利用ps -aux 找出我们的java线程41,然后再用jstack -l 41,就可以查看jvm此刻运行的所有线程.
下面是截取的两个jvm运行的普通线程,一个是守护线程,另外一个是用户线程.
守护线程 守护线程是指给程序提供通用性支持的线程,他不属于程序,gc就是一个很称职的守护线程.守护线程是为用户线程提供服务的,也就是说如果没有用户线程,守护线程就没有存活下去的意义,在jstack中查出来的线程信息中,守护线程有个 daemon 的标志
用户线程 用户线程通常是程序自己开启的.jvm会随着所有的用户程序关闭而关闭
在下面的线程信息中 :
① HikariPool-1 connection closer 是线程的名字,在Java中可以通过Thread.currentThread().getName()来查看线程名字
② prio 应该是线程的优先级
③ tid jvm中的线程id
③ nid tid映射的操作系统中的线程id,非常有用,不过这里是用16进制的表示, 可以通过 printf "%x\n" 十进制数字 查找一个十进制数字的十六进制表示
④ 0x00007fa735a2a000 线程栈的起始地址
⑤ TIMED_WAITING 线程状态
⑥ 0x00000006e941b160 资源名称,等待某个资源被释放,说明有其他线程锁住了该资源,一般是 locked 0x00000006e941b160
线程状态
假如java进程经常出现卡慢,cpu经常会爆满,这时候我们考虑一下是否是我们某些线程太占cpu,导致其他线程不能好好工作.可以通过以下步骤观察
通过top和jstack确定哪些线程耗尽了CPU?这些线程在做什么
1. 背景
有时,线上集群load会突然飙升,无法响应正常请求。
那么引起load飙升的线程究竟在做什么?哪些线程霸占了CPU?可以通过top和jstack命令进行定位。
2. 定位步骤
1. 使用终端1进入目标机器,执行top命令,默认是进程视图,其中PID是进程号,截图如下:
在这里,我们只能看到java进程占用CPU达到115%,那么究竟是那些线程非常耗CPU呢?
2. 由于我们要看到线程,在终端1,按下“H”键或者“shift+h”,top视图会切换到线程视图,其中PID是线程号,截图如下:
可以发现红框内的线程的CPU使用率非常高,占用CPU时间达到1秒左右,显然不正常,但是这些线程在做什么?
3.
打开终端2,使用jstack命令输出这一时刻的线程栈,保存到文件,命名为jstack.log。注意:输出线程栈和保存top命令快照尽量同时进行。
4. 由于jstack.log文件记录的线程ID是16进制,需要将top命令展示的线程号转换为16进制,以15100为例,在linux下输入命令:printf 0x%x 15100,得到15100的十六进制为0x3afc
5. 在jstack.log中搜索0x3afc关键字,可以清晰看到该线程在做刷新地址列表,如下图:
3. 总结
以前碰到集群load飙升时,有时会束手无策,不知从何查起。以后再发生类似问题时,可以使用这个方法,看下究竟是那些线程在长时间占用CPU,尽快定位问题和解决问题。
CPU占用过高,jstack导出后没看出有什么问题,求教
同时按Ctrl ,Alt,del(小数点),打开任务管理器,看看是那个进程占用高,在百度上查一下是什么程序,没用的果断删除
记一次线上机器CPU飙高的排查过程
公司如今把小贷机器都整理回收了,访问量不大,基本都是用户来查看账户进行还款操作。
现在情况是,我们把很多服务都放在了一台服务器上,那天线上环境改了auth的salt,本地这边是写死的,自动上线已经关闭了通道,没辙,手动替包手动上线,结果没多久运维就喊了,表示cpu飙高到300%。
难得的机会,先用top找出cpu占用最多进程
如果想细看进程信息可以使用ll proc
其实运行完这里的时候,我比较吃惊的是,真正占CPU的并不是部署的几个服务,而是resin容器本身,飚到了99%,从这个角度来讲,其实大部分性能问题都是垃圾回收的锅。
然后利用ps查看到的进程pid找cpu最高线程
然后拿着线程tid在jstack找,结果在里面找不到,然后上网查JAVA线程无法在jstack里找到的原因
以上大概意思是没找到线程的跟踪栈有三种情况,就是线程启动前的预启动,以及线程退出后的cleanup,第三种就是,利用JVM TI agent运行的线程,这个线程是用来跑本地code的。
******这里面都提到了JVM TI技术,这个技术主要是用来提供虚拟机调用本地方法的接口,可以获取jvm运行情况和提供本地方法的后门一样的接口工具,一些调试和诊断工具都是基于JVM TI来实现的,很多JVM都有自己的TI实现。
当然,这个Stack Over Flow的答案针对的Java线程,我所知道的是,如果导致我们CPU飙高的并不是java线程,那么jstack -F就打不出来。
而这个jstack -m模式,在官方文档里是说,可以打印出所有Java线程和native frames的所有线程。其实到这里是不是隐隐感觉到什么了~~没错,最后真的是垃圾回收的锅。
然后jdk8利用jstack -m找到了,发现里面使用的方法是CMS垃圾回收器的方法
讲真,平时只负责上下线,JVM配置只要不出问题很少留意,这个年代了,还在使用CMS吗?嗯,jmap看了下,是CMS。而占用大多CPU也是CMS的特性之一:最小化GC中断,付出的代价便是高CPU负荷。
既然定位到了垃圾回收,那就接着排查垃圾回收吧,pid是18637
就去jstat -gc 18637 5000
jstat -gcold 18637
jstat -gcoldcapacity 18637
发现full gc非常频繁(每五秒输出,发现触发fullGC13次)
(复盘这次的排查问题,发现图片保存少之又少。。)
如此频繁,顺带gccause一下看看最近一次回收的原因
结果长这样
从这个结果来看最近一次是Metadata的GC,而触发原因是因为达到了阈值。再细看当前元空间使用率已经接近98%。
到这里不能等了,找运维拿来了jvm参数,有三个参数很亮眼
老年代的配置属于默认配置,占整个堆内存的2/3,CMS回收要达到80%才会触发,我们还没有达到这个值。
那么根据图1中的显示,metaspace我们已经达到了阈值。
这种情况产生也没毛病,想想一台机器部署了5个web服务和5个微服务的场景吧,虽然没什么人访问,但加载的class信息也足够多了。
主要就是metadata的配置,而有关metadata的配置并没有oracle的官方指导,官方指导上的意思是说metaspace的配置,主要是避免首次启动时对加载的类信息做回收,取决于应用本身,够用就行了,不要太频繁触发full gc就好
其实调大这metaspace的配置能够大概解决问题,但是最终我选择了切换为G1垃圾回收。
官方文档是这样写的
首先戳我的点的主要原因是g1最终的目的是要取代cms垃圾回收器,它的回收范围是整个堆,紧跟潮流总不会错的。其次g1取代cms的情况官方也建议了以下几点:
1. 一个是要管理的堆内存大于6g
2. 一个是服务已使用的堆内存超过了50%
3. 一个是对象分配率过高或对象从新生代晋升到老年代速率过快(这里我查了一下资料,意思是对象分配率过高的话其实会导致Minor GC频繁,而这种情况会使对象更快地晋升到老年代,而老年代如果过快地被填充,又会触发FullGC。从设计角度来讲,之所以分代回收,是为了应对对象存活时间而使用不同的回收策略,老年代可不是为了频繁回收而产生的)
4. 垃圾回收导致服务中断时长超过0.5-1s。
这种时候官方就建议把cms换成g1了。
最终cpu骤降,从99%降至一般运行状态下的2%左右。好了。就这样吧,虽然不希望线上出现这种问题,但一旦出问题了,搞一遍还蛮带感的。期待下一次的摸排:DDDDD
后记:
最近在复盘这次垃圾回收,因为这次jvm排查的经历让我对垃圾回收机制和分代回收这一块加深了印象。复盘的时候发现自己思考地还欠缺深入。这次的问题有两个地方值得深思,一个是为什么metaspace会超?如果正常进行GC,为何会超出阈值?其次是没有调大metaspace的前提下,换成G1为什么就没有再频繁FullGC过?
为什么metaspace会超,我经过比对当时留下CMS的jstat数据和G1当前的数据,发现新生代CMS划分区域非常大,是现在G1的十倍。G1是不建议设定新老年代比例的,这样它就可以动态调整新老年代比例,达到最优实践。首先说明metaspace是存储类描述信息的,当堆上分配了对象,就会关联到metaspace里面的类信息,如果堆上的对象不回收,那么metaspace里面的类信息也不会回收。
在配置cms时,我们会设置新生代老年代比例,新生代空间是固定的,如果新生代的空间比较大,回收间隔时间长,那就会导致metaspace里的class信息无法被释放,最终导致未进行youngGC而率先因为metaspace超了跑了FullGC。但G1的新生代空间是根据使用情况动态分配的,它的算法会去回收最有回收价值的region。
就这次修改参数而言,使用G1比CMS时间短很多,就已经执行了717次youngGC,而CMS用了那么久,才52次,侧面也能佐证这个猜测的正确性,解释了为何在相同metaspace的配置下,G1没有产生频繁FullGC的原因:新生代对象回收加快,metaspace空间得到了尽快释放,没有达到阈值,于是不会触发FullGC。
参考文献:
*
*
*
*
*
JVM调优jstack怎么找出最耗cpu的线程并定位代码
第一步:先找出java的进程Id(PID) 假设java应用名称是zcg_commodity
ps -ef|grep zcg_commodity
得到进程Id为32464
第二步:找出该进程内最消耗CPU的线程
top -Hp pid
输入top -Hp 32464
TIME列就是各个java线程耗费的CPU的时间,比如图中是线程ID的为2012的线程,
通过 printf “%x\n” 2012
得到2012的十六进制为 7dc
第三步:
一般会进到jdk的bin目录下,root权限执行
jstack 32464|grep 7dc