jvm总结五《线上故障排查基本操作》

本文讲写一些线上问题的分析和排查,也仅限于基本操作,因为线程问题太多太奇怪,就算是大佬也会碰到棘手的问题。更重要的是,身为楼主的我水平有限。。。只能把自己的经验以及听的看的总结一下,希望碰到问题的时候会有思路。
对于 Java 程序员来讲,排查线上问题是不可避免的。常在河边走,哪有不湿鞋。突然面临 CPU 飚高,内存溢出,频繁 GC,系统卡顿 等等类似问题的时候。我们该怎么做?怎么去解决这些问题?

首先,出现问题,肯定要先定位问题所在,然后分析问题原因,再然后解决问题,最后进行总结,防止下次再次出现。

Java 服务常见线上问题

线上问题重点关注对象:CPU、内存
服务器排查命令: top命令关注CUP、free命令关注内存
注:由于本人环境问题没有截图,以下截图均为网络截图

一、CPU飙高

线上CPU飙高应该是常见的的问题了
思路:首先定位到CPU飙高是哪个java进程,然后找到这个进程中占用最高的java线程,最后根据这个线程的堆栈信息去找到问题代码,然后对代码进行排查,从而解决问题。
操作:

  1. top命令,找到占用CPU最高的java进程
    在这里插入图片描述
    top 命令显示了各个进程 CPU 使用情况,从高到低进行排列。 Load Average 显示最近 1 分钟、5 分钟和 15 分钟的系统平均负载,上图各值为 2.46,1.96,1.99。
标签 简介
PID 进程 id
USER 进程所有者
PR 进程所有者
NI nice 值。负值表示高优先级,正值表示低优先级
VIRT 进程使用的虚拟内存总量,单位 kb。VIRT=SWAP+RES
RES 进程使用的、未被换出的物理内存大小,单位 kb。RES=CODE+DATA
SHR 共享内存大小,单位 kb
S 进程状态。D= 不可中断的睡眠状态 R= 运行 S= 睡眠 T= 跟踪 / 停止 Z= 僵尸进程
%CPU 上次更新到现在的 CPU 时间占用百分比
%MEM 进程使用的物理内存百分比
TIME+ 进程使用的 CPU 时间总计,单位 1/100 秒
COMMAND 进程名称

这里我们找到了占用cpu最高的是java进程,进程PID为11506。
2. 通过 ps -mp pid -o THREAD,tid,time这个命令,找出11506进程中占用最高的线程
在这里插入图片描述
从上图我们可以看到,CPU使用率最高的线程是11508,占了96.6%
将11508转换为 16 进制形式 (因为 java native 线程以 16 进制形式输出),可通过 printf “%x\n” 11508来转16进制
然后通过jstack -l 11508>jstack.log把该线程的堆栈信息dump到一个日志志文件中,然后通过刚刚那个线程id的16进制码去这个堆栈日志文件中进行分析,看看是不是某个业务发生了死循环,我朋友他们是做电商的,听到他们说的最多的就是发生死锁,所以,如果碰到这类问题,可以先去排查一下系统是否发生死锁。
如果没有死锁的的话看一下是否有疯狂的GC,之前有位小哥他们的CPU飙高是因为通过HttpClient访问三方服务,他们这个HttpClient还没有设置超时时间并且他们那个业务还不停的创建对象从而导致jvm不停GC把cpu飙到极限。。可见CPU飙高不一定是什么原因造成的,索引需要掌握一下常用的方法来分析原因。

二、内存问题排查

上面说的是CPU飙高,很大的原因是因为我们自己挖的坑。
我们所说的内存问题通常就是GC问题
有 2 种情况,一种是内存溢出了,一种是内存没有溢出但是GC不健康

内存溢出的情况可以通过加上 -XX:+HeapDumpOnOutOfMemoryError 参数,该参数作用是:在程序内存溢出时输出 dump 文件。
有了 dump 文件,就可以通过 dump 分析工具进行分析了,比如常用的 MAT,Jprofile,jvisualvm 等工具都可以分析,这些工具都能够看出到底是哪里溢出,哪里创建了大量的对象等等信息。

第二种情况就比较复杂了,GC不健康
一个健康的GC
YGC 5 秒一次左右,每次不超过 50 毫秒,FGC 最好没有,CMS GC 一天一次左右。

GC 的优化有 2 个维度,一是频率,二是时长。
我们看 YGC,首先看频率,如果 YGC 超过 5 秒一次,甚至更长,说明系统内存过大,应该缩小容量,如果频率很高,说明 Eden 区过小,可以将 Eden 区增大,但整个新生代的容量应该在堆的 30% - 40% 之间,eden,from 和 to 的比例应该在 8:1:1 左右,这个比例可根据对象晋升的大小进行调整。

如果 YGC 时间过长呢?YGC 有 2 个过程,一个是扫描,一个是复制,通常扫描速度很快,复制速度相比而言要慢一些,如果每次都有大量对象要复制,就会将 STW 时间延长,还有一个情况就是 StringTable ,这个数据结构中存储着 String.intern 方法返回的常连池的引用,YGC 每次都会扫描这个数据结构(HashTable),如果这个数据结构很大,且没有经过 FGC,那么也会拉长 STW 时长,还有一种情况就是操作系统的虚拟内存,当 GC 时正巧操作系统正在交换内存,也会拉长 STW 时长。

再来看看 FGC,实际上,FGC 我们只能优化频率,无法优化时长,因为这个时长无法控制。如何优化频率呢?
首先,FGC 的原因有几个,1 是 Old 区内存不够,2 是元数据区内存不够,3 是 System.gc(), 4 是 jmap 或者 jcmd,5 是 CMS Promotion failed 或者 concurrent mode failure,6 JVM 基于悲观策略认为这次 YGC 后 Old 区无法容纳晋升的对象,因此取消 YGC,提前 FGC。
通常优化的点是 Old 区内存不够导致 FGC。如果 FGC 后还有大量对象,说明 Old 区过小,应该扩大 Old 区,如果 FGC 后效果很好,说明 Old 区存在了大量短命的对象,优化的点应该是让这些对象在新生代就被 YGC 掉,通常的做法是增大新生代,如果有大而短命的对象,通过参数设置对象的大小,不要让这些对象进入 Old 区,还需要检查晋升年龄是否过小。如果 YGC 后,有大量对象因为无法进入 Survivor 区从而提前晋升,这时应该增大 Survivor 区,但不宜太大。

上面说的都是优化的思路,我们也需要一些工具知道 GC 的状况。
JDK 提供了很多的工具,比如 jmap ,jcmd 等,oracle 官方推荐使用 jcmd 代替 jmap,因为 jcmd 确实能代替 jmap 很多功能。jmap 可以打印对象的分布信息,可以 dump 文件,注意,jmap 和 jcmd dump 文件的时候会触发 FGC ,使用的时候注意场景。
还有一个比较常用的工具是 jstat,该工具可以查看 GC 的详细信息,比如 eden ,from,to,old 等区域的内存使用情况。
还有一个工具是 jinfo,该工具可以查看当前 jvm 使用了哪些参数,并且也可以在不停机的情况下修改参数。
包括我们上面说的一些分析 dump 文件的可视化工具,MAT,Jprofile,jvisualvm 等,这些工具可以分析 jmap dump 下来的文件,看看哪个对象使用的内存较多,通常是能够查出问题的。

还有很重要的一点就是,线上环境一定要带上 GC 日志!!!

线上问题多而复杂,不是一两篇文章能够说完的,我们要掌握的是方法,有了正确的方法,遇到什么样的问题都可以有思路,还有一点经验很重要。碰到问题解决之后做好总结。

希望各位猿星人都能 上线大吉,少出BUG

猜你喜欢

转载自blog.csdn.net/u010994966/article/details/103011654
今日推荐