岩像 發表於 2025-8-18 00:00:00

Linux服务器快速定时CPU占用高线程的方法

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>场景描述</li><li>问题模拟</li><li>问题处理</li></ul></div><p class="maodian"></p><h2>场景描述</h2>
<p>最近遇到一个生产环境,一个程序因为代码写的有问题,导致CPU占用很高,所以需要马上排查问题,首先要先找出哪个程序,具体代码在哪里,所以需要借助<code>jstack</code>、<code>jmap</code>这些命令来定位具体的线程,查看具体的线程堆栈信息</p>
<p class="maodian"></p><h2>问题模拟</h2>
<p>为了模拟生产环境问题,需要写点代码,让AI写吧,但是不要oom,只是让cpu高就行,AI写的代码,接口传一个比较大的值,比如10000</p>
<div class="dxycode"><pre class="brush:java;">package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping(value = "/test")
public void test(@RequestParam(value = "num", defaultValue = "10000")Integer num) {
// 定义线程数量,这里设置为CPU核心数的两倍,以确保高CPU使用率
int numberOfThreads = Runtime.getRuntime().availableProcessors() * 2;
Thread[] threads = new Thread;
// 创建并启动线程
for (int i = 0; i &lt; numberOfThreads; i++) {
threads = new Thread(new IntensiveTask(num));
threads.start();
}
}
static class IntensiveTask implements Runnable {
private Integer num = 10000;
public IntensiveTask(Integer num) {
this.num = num;
}
@Override
public void run() {
while (true) {
// 创建一个大型数组
int[] array = new int;
// 使用计算密集型操作,如排序
java.util.Arrays.sort(array);
// 短暂休眠以避免完全占用CPU
try {
Thread.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
}
}
}</pre></div>
<p>将代码丢到开发环境,java启动程序</p>
<div class="dxycode"><pre class="brush:bash;">nohupjava-jar demo-0.0.1-SNAPSHOT.jar &gt; /opt/logs/output.log 2&gt;&amp;1&amp;</pre></div>
<p>调用接口:</p>
<div class="dxycode"><pre class="brush:bash;">curl http://127.0.0.1:8080/test?num=10000</pre></div>
<p class="maodian"></p><h2>问题处理</h2>
<p>在window系统有任务管理器这些可视化界面可以看,在Linux服务器也有,比如<code>top</code>命令</p>
<p>定位CPU占用高的进程</p>
<p>在Linux服务器,可以使用top命令,其中<code>%CPU</code>表示cpu占用率,<code>%MEM</code>表示内存占用率</p>
<div class="dxycode"><pre class="brush:bash;">top</pre></div>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20250817/2-250QG62503225.png" /></p>
<p>直接使用<code>top</code>来看有时候不能很直观,所以加点命令,</p>
<div class="dxycode"><pre class="brush:bash;">top-b-n1|head-n16</pre></div>
<p>-b选项表示批处理模式,-n 1表示只运行一次top命令。head -n 16表示打印16行,这个行数根据具体情况加,因为我想打印出前10的进程</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20250817/2-250QG62504611.png" /></p>
<p>想要持续监控CPU和内存占用最高的前10个进程,加上<code>watch</code>命令,每1s会更新一次</p>
<div class="dxycode"><pre class="brush:bash;">watch-n1"top -b -n 1 | head -n 16"</pre></div>
<p>也可以使用命令:</p>
<div class="dxycode"><pre class="brush:bash;">top-b-n1|head-n20|awk'NR&gt;1 {print $1, $2, $9, $10, $12}'</pre></div>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20250817/2-250QG62504O6.png" /></p>
<p>定位进程中高CPU占用的线程</p>
<p>在前面的方法中使用top命令定位到进程后,需要再定义进程中具体的线程,可以使用命令,其中pid就是前面定位到的进程ID</p>
<div class="dxycode"><pre class="brush:bash;">top-Hp pid</pre></div>
<p>比如前面定位到2540这个进程,监控一下进程下面具体的线程,发现线程2568、2569比较占cpu</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20250817/2-250QG625043c.png" /></p>
<p>嫌弃不够直观,直接打印一下前10,2540是进程ID</p>
<div class="dxycode"><pre class="brush:bash;">ps-mp2540-o THREAD,tid |gawk'NR!=1 &amp;&amp; NR!=2 { printf "%s %x\n",$2,$8 }'|sort-rn|head-10</pre></div>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20250817/2-250QG62504164.png" /></p>
<p>将线程ID转换为十六进制</p>
<p>将前面的线程ID转为十六进制</p>
<div class="dxycode"><pre class="brush:bash;">printf"%x\n"2569</pre></div>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20250817/2-250QG62504552.png" /></p>
<p>获取线程堆栈信息</p>
<div class="dxycode"><pre class="brush:bash;">jstack 2540|grep"a09"-A30</pre></div>
<p>通过线程堆栈信息就可以定位到具体的代码</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20250817/2-250QG62504639.png" /></p>
<p>找到具体问题代码,原来写了个while(true)循环</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20250817/2-250QG62504532.png" /></p>
<p>监控GC情况</p>
<p>当然生产环境的问题不一定这么容易定位,所以需要监控gc情况,使用jstat -gcutil &lt;进程号&gt;命令查看GC持续变化情况,如果发现Full GC次数过多,可能需要进一步分析内存使用情况</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20250817/2-250QG62504148.png" /></p>
<p>导出堆内存文件</p>
<p>如果出现频繁Full GC的问题,使用命令导出堆内存文件:</p>
<div class="dxycode"><pre class="brush:bash;">jmap -dump:format=b,file=heapdump.hprof pid</pre></div>
<p>分析堆内存文件</p>
<p>使用MAT工具打开导出的heapdump.hprof文件,分析内存泄漏和内存分配情况,找出占用大量内存的对象</p>
<p>优化代码</p>
<p>根据MAT的分析结果,定位代码中的问题,优化代码</p>
<p>监控生产环境</p>
<p>重新部署修改后的代码,并使用top命令监控CPU占用情况,确保问题得到解决,同时可以部署一下监控平台,比如zabbix等对生产环境服务器进行监控,及时发现问题</p>
<p>以上就是Linux服务器如何快速定时CPU占用高的线程的详细内容,更多相关资料请阅读琼殿技术社区其它文章!</p>
頁: [1]
查看完整版本: Linux服务器快速定时CPU占用高线程的方法