java应⽤服务占⽤cpu过⾼,如何优化
转载链接:
当我们cpu使⽤率⾼的情况下会出现什么情况?
我们访问程序的速度⽐较慢,运⾏时间长。
系统崩溃,⽆法访问程序。
什么情况会导致Java应⽤程序的CPU使⽤率飙升?
解决这个问题之前我们先了解⼏个知识点:
1.如何计算CPU使⽤率?
CPU%= 1 - idleTime / sysTime * 100
idleTime:CPU空闲的时间
sysTime:CPU处于⽤户模式和内核模式的时间总和
2.与CPU使⽤率有关的是什么?
⼈们常说,计算密集型程序的CPU密集程度更⾼。
那么,JAVA应⽤程序中的哪些操作更加CPU密集?
以下列出了常见的CPU密集型操作:
频繁的GC; 如果访问量很⾼,可能会导致频繁的GC甚⾄FGC。当调⽤量很⼤时,内存分配将如此之快以⾄于GC线程将连续执⾏,这将导致CPU飙升。
序列化和反序列化。稍后将给出⼀个⽰例:当程序执⾏xml解析时,调⽤量会增加,从⽽导致CPU变满。
序列化和反序列化;
正则表达式。我遇到了正则表达式使CPU充满的情况; 原因可能是Java正则表达式使⽤的引擎实现是NFA⾃动机,它将在字符匹配期间执⾏回溯。我写了⼀篇⽂章“ 正则表达式中的隐藏陷阱 ”来详细解释原因。
线程上下⽂切换; 有许多已启动的线程,这些线程的状态在Blocked(锁定等待,IO等待等)和Running之间发⽣变化。当锁争⽤激烈时,这种情况很容易发⽣。
有些线程正在执⾏⾮阻塞操作,例如while (true)语句。如果在程序中计算需要很长时间,则可以使线程休眠。
⽆限循环的while会导致CPU使⽤率飙升
经常使⽤Young GC会导致CPU占⽤率飙升
3. CPU是否与进程和线程相关?
现在,分时操作系统使⽤循环⽅式为进程调度分配时间⽚。如果进程正在等待或阻塞,那么它将不会使⽤CPU资源。线程称为轻量级进程,并共享进程资源。因此,线程调度在CPU中也是分时的。但在Java中,我们使⽤JVM进⾏线程调度。因此,通常,线程调度有两种模式:时间共享调度和抢占式调度。
情况1:while的⽆限循环会导致CPU使⽤率飙升吗?
是。
⾸先,⽆限循环将调⽤CPU寄存器进⾏计数,此操作将占⽤CPU资源。那么,如果线程始终处于⽆限循环状态,CPU是否会切换线程?
除⾮操作系统时间⽚到期,否则⽆限循环不会放弃占⽤的CPU资源,并且⽆限循环将继续向系统请求时间⽚,直到系统没有空闲时间来执⾏任何其他操作。
情况2:频繁的Young GC会导致CPU占⽤率飙升吗?
是。
Young GC本⾝就是JVM⽤于垃圾收集的操作,它需要计算内存和调⽤寄存器。因此,频繁的Young GC必须占⽤CPU资源。
让我们来看⼀个现实世界的案例。for循环从数据库中查询数据集合,然后再次封装新的数据集合。如果内存不⾜以存储,JVM将回收不再使⽤的数据。因此,如果所需的存储空间很⼤,您可能会收到CPU使⽤率警报。
情况3:具有⼤量线程的应⽤程序的CPU使⽤率是否较⾼?
不是。
如果通过jstack检查系统线程状态时线程总数很⼤,但处于Runnable和Running状态的线程数不多,则CPU使⽤率不⼀定很⾼。
我遇到过这样⼀种情况:系统线程的数量是1000+,其中超过900个线程处于BLOCKED和WAITING状态。该线程占⽤很少的CPU。
但是⼤多数情况下,如果线程数很⼤,那么常见的原因是⼤量线程处于BLOCKED和WAITING状态。
情况4:对于CPU占⽤率⾼的应⽤程序,线程数是否较⼤?
不是。
⾼CPU使⽤率的关键因素是计算密集型操作。如果⼀个线程中有⼤量计算,则CPU使⽤率也可能很⾼。这也是数据脚本任务需要在⼤规模集上运⾏的原因。
情况5.处于BLOCKED状态的线程是否会导致CPU占⽤率飙升?
不会。
CPU使⽤率的飙升更多是由于上下⽂切换或过多的可运⾏状态线程。处于阻塞状态的线程不⼀定会导致CPU使⽤率上升。
情况6.如果分时操作系统中CPU的值us或sy值很⾼,这意味着什么?
您可以使⽤命令查CPU的值us和sy值top,如以下⽰例所⽰:
us:⽤户空间占⽤CPU的百分⽐。简单来说,⾼我们是由程序引起的。通过分析线程堆栈很容易到有问题的线程。
sy:内核空间占⽤CPU的百分⽐。当sy为⾼时,如果它是由程序引起的,那么它基本上是由于线程上下⽂切换。
由此我们得出结论CPU使⽤率飙升的两⼤原因:
⽆限循环的while会导致CPU使⽤率飙升
经常使⽤Young GC会导致CPU占⽤率飙
4.为什么cpu飙升到100%但是程序还是可以访问?
我们可以看到的当前服务器的cpu已经达到了接近400%,为什么会达到400%了,因为我们系统是四核的,每个cpu都飙升到100%,所以看到的是接近400%,但是我继续访问接⼝,发现还是可以访问,
原因是什么呢?因为cpu通过时间⽚来循环执⾏任务的,多个任务之间是相关切换的,当切换到我们访问接⼝的任务时,我们就可以访问了。但是如果继续增加访问达到cpu不能承受的范围内,系统肯定会崩溃。
在linux系统下如何排查cpu使⽤率飙升的原因?
1、使⽤top命令查看cpu利⽤率
如下图:
2、如何查看当前系统的cpu的数量
由于我们现在的系统都不是单核的,都是多核的,每个核中都由⼀个cpu,我们可以在使⽤top命令后输⼊1就可以看到我们当前系统下的cpu 的数量。我们可以看到cpu0和cpu1说明系统是双核的。如下图:
我们也可以使⽤通过proc⽂件系统,直接获取cpu总数量,具体执⾏如下命令:cat /proc/cpuinfo  | grep processor 。我们可以到由两个processor,说明系统是双核的。如下图:
3、top命令下各个参数的意思
top命令的结果分为两个部分:
统计信息:前五⾏是系统整体的统计信息;
进程信息:统计信息下⽅类似表格区域显⽰的是各个进程的详细信息,默认5秒刷新⼀次。
统计信息参数介绍
进程信息参数介绍
在top命令中按f按可以查看显⽰的列信息,按对应字母来开启/关闭列,⼤写字母表⽰开启,⼩写字母表⽰关闭。带*号的是默认列。
A: PID = (Process Id) 进程Id;
E: USER = (User Name) 进程所有者的⽤户名;
H: PR = (Priority) 优先级
I: NI = (Nice value) nice值。负值表⽰⾼优先级,正值表⽰低优先级
O: VIRT = (Virtual Image (kb)) 进程使⽤的虚拟内存总量,单位kb。
VIRT=SWAP+RES
Q: RES = (Resident size (kb)) 进程使⽤的、未被换出的物理内存⼤⼩,单位kb。
RES=CODE+DATA
T: SHR = (Shared Mem size (kb)) 共享内存⼤⼩,单位kb
W: S = (Process Status) 进程状态。D=不可中断的睡眠状态,R=运⾏,S=睡眠,T=跟踪/停⽌,Z=僵⼫进程
K: %CPU = (CPU usage) 上次更新到现在的时间CPU占⽤百分⽐
N: %MEM = (Memory usage (RES)) 进程使⽤的物理内存百分⽐
M: TIME+ = (CPU Time, hundredths) 进程使⽤的CPU时间总计,单位1/100秒
b: PPID = (Parent Process Pid) ⽗进程Id
c: RUSER = (Real user name)
d: UID = (User Id) 进程所有者的⽤户id
f: GROUP = (Group Name) 进程所有者的组名
g: TTY = (Controlling Tty) 启动进程的终端名。不是从终端启动的进程则显⽰为 ?
j: P = (Last used cpu (SMP)) 最后使⽤的CPU,仅在多CPU环境下有意义
p: SWAP = (Swapped size (kb)) 进程使⽤的虚拟内存中,被换出的⼤⼩,单位kb
l: TIME = (CPU Time) 进程使⽤的CPU时间总计,单位秒
r: CODE = (Code size (kb)) 可执⾏代码占⽤的物理内存⼤⼩,单位kb
s: DATA = (Data+Stack size (kb)) 可执⾏代码以外的部分(数据段+栈)占⽤的物理内存⼤⼩,单位kb
u: nFLT = (Page Fault count) 页⾯错误次数
v: nDRT = (Dirty Pages count) 最后⼀次写⼊到现在,被修改过的页⾯数
y: WCHAN = (Sleeping in Function) 若该进程在睡眠,则显⽰睡眠中的系统函数名
z: Flags = (Task Flags)任务标志,参考 sched.h
步骤⼀、到最耗CPU的进程
执⾏top -c ,显⽰进程运⾏信息列表,键⼊P (⼤写p),进程按照CPU使⽤率排序,我们可以看到的当前服务器的cpu已经达到了接近400%,记录PID为38983的进程号,也可以看到是⼀个java的程序。
步骤⼆:到最耗CPU的线程
输⼊命令top -Hp 38983,显⽰⼀个进程的线程运⾏信息列表,键⼊P (⼤写p),线程按照CPU使⽤率排序,我么们可以看到占⽤cpu最⾼的线程是39060
步骤三:将线程PID转化为16进制
输⼊命令printf “%x” 39060,所以要转化为16进制,是因为堆栈⾥,线程id是⽤16进制表⽰的。
步骤四:查看堆栈,到该线程分析
输⼊命令jstack 38983| grep ‘0x9894’ -C5 --color,打印进程堆栈,通过线程id,过滤得到线程堆栈
时间正则表达式java步骤五:根据提⽰,修改代码
这就证实了while的⽆限循环会导致CPU使⽤率飙升。我们在实际开发中要在本地充分的测试,然后再
上线,如果线上出现了问题,也不能慌,⼀定要稳住,要赶紧版本回滚。然后在慢慢排查问题进⾏修改。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。