Linux - 通过工具查询项目性能瓶颈

在大型项目中,如果项目出现性能瓶颈,我们该如何快速从内存泄露、CPU、IO、锁、网络几个层面来突破

内存泄露排查

如何导致内存泄露

僵尸进程

子进程退出后父进程不回收且没有设置子进程SIGCHILD为SIG_IGN让子进程自己被操作系统回收,就会一直处于僵尸进程,导致PCB内存一直占用导致内存泄露

这个内存泄露和其他的比较特别,我直接在这里讲解怎么排查:我们直接用ps搜索即可,通过下面两条指令可以查看僵尸进程数量,如果数量迟迟很高,那么说明有问题。

bash 复制代码
# 1. 查看所有僵尸进程
ps aux | awk '$8=="Z" {print $0}'

# 2. 统计僵尸进程数量
ps -eo stat | grep -c "^Z"

文件描述符泄露

这个也比较特别,文件开了没有关,导致文件描述符被占满,最后导致后续申请文件描述符没有多的了。出现内存泄露。更多出现在网络编程里面,使用了socket编程没有关闭链接等导致。如果使用C++IO库,创建的stream对象在生命周期结束后会自动关闭文件描述符。

这个我也直接在这讲解了:

首先我们给一个fd泄露的代码

cpp 复制代码
#include<thread>
#include<sys/types.h>
#include<sys/socket.h>
using namespace std;

int main(){
    while(true){
        this_thread::sleep_for(chrono::milliseconds(1000));
        int fd = socket(AF_INET,SOCK_DGRAM,0);
    }
    return 0;
}

我们可以直接通过/proto/<pid>/fd来查看

此时发现有异常的fd。我们就可以知道有fd泄露了。

线程栈泄露

我们创建线程后,没有最后使用join导致线程栈迟迟未被回收,此时就会导致内存泄露

这个多发生于pthread库使用。

如果是C++的并发库则不会,如果没有join程序跑起来会直接报错终止。而且C++20加入了jthread析构时自动join线程

new了没delete

这个是我们最常见的错误,导致堆区内存迟迟不释放。排查方式下面统一讲。

避免方式就是使用智能指针。

mmap了没munmap

这个也是比较容易出错的,导致共享区内存不释放。排查方式下面统一讲。

避免方式还是智能指针

shared_ptr循环引用

这个是智能制造使用是出现的循环引用问题,这个下面单独讲

内存可能短时间内不会带来什么影响,如果内存泄露,最后内存不足,就会导致大量的内存进行swap交换,造成严重的性能下降。最后内存swap交换区也塞不下,导致服务器被杀掉。这是很严重的事情。因此我们需要相关工具来排查。

我们先来写一个内存泄露的代码:

cpp 复制代码
#include<thread>
#include<vector>
#include<sys/mman.h>
using namespace std;

int main(){
    vector<void*>vec;
    unsigned long long gap=0;
    while(true){
        this_thread::sleep_for(chrono::milliseconds(100));
        void* arr = mmap(nullptr,4*1024,PROT_READ|PROT_WRITE,MAP_ANON|MAP_PRIVATE,-1,0);
        if(gap&3)munmap(arr,4*1024);
        ++gap;
    }
    return 0;
}

每四次我们就不回收一次内存。这样就会出现内存泄露

top/htop

top

是比较轻量级的查看动态资源的工具。他也可以用于CPU等资源占比的查看,这里我们先介绍内存的

top是黑白的,界面不是特别友好,但是还是可以看出内存占比高低

通过这些选项可以进行不同的操作

htop

htop是比top更高级的工具,界面更加可视化

不需要手动输指令选项,通过FN来操作。大家可以自行尝试,这里不做详解了,因为大家只要按一遍就知道怎么操作了。

通过top/htop我们就可以看到内存占比比较大的进程了,如果内存持续上升我们就要重点关注某些进程了。

pmap -x

通过这个可以查看进程的内存分布,判断是哪个内存区域在暴涨内存。至于为什么加上-x,因为加上-x才能显示全部内存信息。不加看不全

这里我们启动这个内存泄露的进程,并在后续监控中发现它内存的异常。此时我们开始对其进行更加细致的监测

-p选项可以指定要监视进程的pid

然后就可以得到内存排布图,但是这样的静态图标是发现不到端倪的。

watch

通过watch -n t(t时间间隔更新) 要执行的指令

通过这个我们就可以动态监测了:

我们可以看到有一个anon类型的内存在持续上涨(Mapping是页面类型,有stack,库内存,anon等)

anon是私有内存,如堆区、共享内存、线程栈等。

此时我们确实找到了有异常的内存增长,因此我们要具体判断到底是谁在泄露内存,没有内存回收。

我们知道了有内存泄露了,我们此时要开始调查具体是是什么类型的泄露了。

首先线程栈我们直接可以通过使用C++并发库避免,这里就不说了。

/proc/<pid>/smaps

然后是如果区分共享内存和堆区内存,我们通过查看/proc/<pid>/smaps来看相关信息

它存储了已经申请内存的信息,通过这个可以查看哪个区域内存已经占据了特别多

strace命令检测系统调用

strace -e -trace=(系统调用名字,系统调用名字) -p <PID>

通过查看统计(这里没有统计,真正项目里面调用十分混乱,要通过相应统计系统调用此时来判断)这里我们就可以看到是munmap没有正确调用导致的内存泄露。

当然我们也可以通过这个检测brk的堆内存申请,来判断是否是堆内存的问题。

valgrind

可以使用valgrind工具来检测内存泄露,循环引用的问题可以使用它来检测,这个我具体不讲了,太多了。大家自行搜索

  • 优点:简单、无需重新编译、通用

  • 缺点:性能损耗大(10~100倍),不能线上用

tcmalloc

tcmalloc等内存池项目自带了内存快照,性能损耗极低,可以线上用。

内存泄露避免方式

内存池、智能指针正确使用、做好子进程回收等

CPU爆满排查

top/htop

通过这个查看爆满的进程,这个上面讲过,不多说了

perf

通过perf top -p <PID>进行进程各个函数CPU使用率的检测

这里我们发现大量的都是kernel内核在占用CPU,说明系统调用占比大。就要做对应优化。

另外,如果是用户级代码CPU占比大,可以看到具体是什么函数使用了这么多。就可以进行对应的函数算法数据结构等优化。

IO高

iostat

可以查看磁盘的IO情况

iostat 1 每1s更新一次

iotop

这个就相当于IO届的top/htop

通过iotop -p <PID>指定监视对象

锁冲突

cpp 复制代码
#include<iostream>
#include<mutex>
#include<thread>
#include<vector>
#include<sys/mman.h>
#include<sys/types.h>
#include<sys/socket.h>
using namespace std;

int main(){
    vector<void*>vec;
    unsigned long long gap=0;
    mutex lock;
    while(true){
        for(int x=0;x<100;++x){
            lock.lock();
            lock.unlock();
        }
        this_thread::sleep_for(chrono::milliseconds(100));
        void* arr = mmap(nullptr,4*1024,PROT_READ|PROT_WRITE,MAP_ANON|MAP_PRIVATE,-1,0);
        if(gap&3)munmap(arr,4*1024);
        ++gap;
    }
    return 0;
}

我们加一个锁

strace

我们依然可以使用stace来查看,因为锁是系统调用,我们通过查看系统调用看其调用次数

perf top

查看对应pthread_mutex_lock/unlock的CPU占比

perf lock

使用 perf lock record -p <PID>来查看锁的上锁解锁时间

根据长短我们需要减少长时间锁,防止其他线程获取不到CPU资源长等待

网络拥塞

netstat/ss

推荐ss比netstat查找更快,两个指令的指令参数是一样的

上面没有列完,还有-s统计类型数量

state 加上状态可以查看特殊状态

通过ss -nltp 查看tcp监听端口

通过上面操作,我们可以对连接数进行检测,看是否出现了拥塞情况。

相关推荐
安科士andxe5 小时前
深入解析|安科士1.25G CWDM SFP光模块核心技术,破解中长距离传输痛点
服务器·网络·5g
小白同学_C8 小时前
Lab4-Lab: traps && MIT6.1810操作系统工程【持续更新】 _
linux·c/c++·操作系统os
今天只学一颗糖8 小时前
1、《深入理解计算机系统》--计算机系统介绍
linux·笔记·学习·系统架构
2601_949146538 小时前
Shell语音通知接口使用指南:运维自动化中的语音告警集成方案
运维·自动化
儒雅的晴天8 小时前
大模型幻觉问题
运维·服务器
Gofarlic_OMS9 小时前
科学计算领域MATLAB许可证管理工具对比推荐
运维·开发语言·算法·matlab·自动化
通信大师9 小时前
深度解析PCC策略计费控制:核心网产品与应用价值
运维·服务器·网络·5g
dixiuapp9 小时前
智能工单系统如何选,实现自动化与预测性维护
运维·自动化
不做无法实现的梦~10 小时前
ros2实现路径规划---nav2部分
linux·stm32·嵌入式硬件·机器人·自动驾驶
Elastic 中国社区官方博客10 小时前
如何防御你的 RAG 系统免受上下文投毒攻击
大数据·运维·人工智能·elasticsearch·搜索引擎·ai·全文检索