操作系统-内存部分-工具使用-内存使用情况-缓存命中情况-检测内存泄漏

如何查看内存使用情况小结:
系统的内存使用、进程的内存使用、缓冲命中情况,内存增长趋势、内存泄露检测

工具-free-查看系统内存使用情况

查看系统整体的内存使用情况

free 的输出示例
在这里插入图片描述
你可以看到,free 输出的是一个表格,其中的数值都默认以字节为单位。表格总共有两行
六列,这两行分别是物理内存 Mem 和交换分区 Swap 的使用情况,而六列中,每列数据的含义分别为:

第一列,total 是总内存大小;
第二列,used 是已使用内存的大小,包含了共享内存;
第三列,free 是未使用内存的大小;
第四列,shared 是共享内存的大小;
第五列,buff/cache 是缓存和缓冲区的大小;
最后一列,available 是新进程可用内存的大小。

这里尤其注意一下,最后一列的可用内存 available 。available 不仅包含未使用内存,还
包括了可回收的缓存,所以一般会比未使用内存更大。不过,并不是所有缓存都可以回收,因为有些缓存可能正在使用中。

free 数据的来源,来自proc

查询man free中的说明书
在这里插入图片描述

在这里插入图片描述

工具-top,查看进程的内存使用情况

top的输出示例

#按下 M 切换到内存排序
在这里插入图片描述
top 输出界面的顶端,也显示了系统整体的内存使用情况,这些数据跟 free 类似,我就不再重复解释。我们接着看下面的内容,跟内存相关的几列数据,比如 VIRT、RES、SHR 以及 %MEM 等。
这些数据,包含了进程最重要的几个内存使用情况,我们挨个来看。

VIRT进程虚拟内存的大小,只要是进程申请过的内存,即便还没有真正分配物理内
存,也会计算在内。
RES常驻内存的大小,也就是进程实际使用的物理内存大小,但不包括 Swap 和共享
内存。
SHR 是共享内存的大小,比如与其他进程共同使用的共享内存、加载的动态链接库以及
程序的代码段等。
%MEM 是进程使用物理内存占系统总内存的百分比。

除了要认识这些基本信息,在查看 top 输出时,你还要注意两点。

第一,虚拟内存通常并不会全部分配物理内存。从上面的输出,你可以发现每个进程的虚拟内存都比常驻内存大得多

第二,共享内存 SHR 并不一定是共享的,比方说,程序的代码段、非共享的动态链接库,
也都算在 SHR 里。当然,SHR 也包括了进程间真正共享的内存。所以在计算多个进程的内
存使用时,不要把所有进程的 SHR 直接相加得出结果。

工具-PS,查看进程的内存使用情况

在这里插入图片描述

问讯飞星火大模型,关于性能工具PS的常见使用示例

1.查看所有进程信息
ps -ef
2.查看指定用户的进程信息
ps -u 用户名
3.查看指定进程ID的进程信息
    ps -p 进程ID
4.查看指定终端的进程信息:
    ps -t 终端号
5.查看指定CPU的进程信息
ps -C 程序名
6.查看指定时间间隔内的进程信息
ps -eo pid,ppid,cmd,%mem,%cpu --sort=-%cpu | head -n 10
这个示例会**显示占用CPU最高的前10个进程的信息**。
7.查看指定进程的线程信息
 ps -T -p 进程ID 
8.查看指定进程的内存信息
ps -o vsz,rss,%mem -p 进程ID
这个示例会显示指定进程的虚拟内存大小、物理内存大小和内存占用百分比

proc 文件系统

/proc 是 Linux 内核提供的一种特殊文件系统,是用户跟内核交互的接口。比方说,用户可以从 /proc 中查询内核的运行状态和配置选项,
查询进程的运行状态、统计数据等,当然,你也可以通过 /proc 来修改内核的配置。
proc 文件系统同时也是很多性能工具的最终数据来源。比如我们刚才看到的 free ,就是通
过读取 /proc/meminfo ,得到内存的使用情况。

继续说回 /proc/meminfo,既然 Buffers、Cached、SReclaimable 这几个指标不容易理
解,那我们还得继续查 proc 文件系统,获取它们的详细定义。

执行 man proc ,你就可以得到 proc 文件系统的详细文档。

注意这个文档比较长,你最好搜索一下(比如搜索 meminfo),以便更快定位到内存部
分。

如何统计出所有进程的物理内存使用量呢?

我们已经知道,可以使用 ps、top 或者 proc 文件系统,来获取进程的内存使用情况。那
么,如何统计出所有进程的物理内存使用量呢?

提示:要避免重复计算多个进程同时占用的内存,像是页缓存、共享内存这类。如果你把
ps、top 得到的数据直接相加,就会出现重复计算的问题。

这里,我推荐从 /proc/< pid >/smaps 入手。前面内容里,我并没有直接讲过 /proc/<
pid >smaps 文件中各个指标含义,所以,需要你自己动手查 proc 文件系统的文档,解读
并回答这个问题。

来自讯飞星火大模型的回答

Rss(Resident Set Size)表示的是进程驻留内存大小,通常指的是进程使用的物理内存大小。在/proc/[pid]/smaps文件中,每个虚拟内存区域(VMA)都会有对应的Rss值,因此会有多行输出。

具体来说,/proc/[pid]/smaps文件展示了一个进程的详细内存使用情况,其中每个VMA都有一系列数据,包括权限、映射的文件等,以及Size(内存区域的大小)、Rss(驻留内存大小)、Pss(比例集大小)等信息。这些信息对于理解进程的内存使用情况非常有帮助,尤其是在排查内存泄漏或性能瓶颈时。

总的来说,/proc/[pid]/smaps文件中的Rss确实有多行输出,因为每个VMA都会有一个Rss值,反映了该内存区域的物理内存使用情况。


在这里插入图片描述

缓存命中率

你想要做成某件事情,结果应该怎么评估?比如说,我们想利用缓存来提升程序的运行效率,应该怎么评估这个效果呢?换句话说,有没有哪个指标可以衡量缓存使用的好坏呢?

缓存的命中率。
所谓缓存命中率,是指直接通过缓存获取数据的请求次数,占所有数据请求次数的百分比。

命中率越高,表示使用缓存带来的收益越高,应用程序的性能也就越好。

实际上,缓存是现在所有高并发系统必需的核心模块,主要作用就是把经常访问的数据(也
就是热点数据),提前读入到内存中。这样,下次访问时就可以直接从内存读取数据,而不
需要经过硬盘,从而加快应用程序的响应速度。

这些独立的缓存模块通常会提供查询接口,方便我们随时查看缓存的命中情况。不过 Linux
系统中并没有直接提供这些接口,所以这里我要介绍一下,cachestat 和 cachetop ,它们
正是查看系统缓存命中情况的工具。

缓存命中情况的工具-cachestat、cachetop

cachestat 提供了整个操作系统缓存的读写命中情况。

cachetop 提供了每个进程的缓存命中情况。

这两个工具都是 bcc 软件包的一部分,它们基于 Linux 内核的 eBPF(extended Berkeley
Packet Filters)机制,来跟踪内核中管理的缓存,并输出缓存的使用和命中情况。

下面就是一个 cachestat
的运行界面,它以 1 秒的时间间隔,输出了 3 组缓存统计数据:
在这里插入图片描述
你可以看到,cachestat 的输出其实是一个表格。每行代表一组数据,而每一列代表不同的
缓存统计指标。这些指标从左到右依次表示:

TOTAL ,表示总的 I/O 次数;
MISSES ,表示缓存未命中的次数;
HITS ,表示缓存命中的次数;
DIRTIES, 表示新增到缓存中的脏页数;
BUFFERS_MB 表示 Buffers 的大小,以 MB 为单位;
CACHED_MB 表示 Cache 的大小,以 MB 为单位。

接下来我们再来看一个 cachetop 的运行界面:
在这里插入图片描述
它的输出跟 top 类似,默认按照缓存的命中次数(HITS)排序,展示了每个进程的缓存命
中情况
。具体到每一个指标,这里的 HITS、MISSES 和 DIRTIES ,跟 cachestat 里的含义
一样,分别代表间隔时间内的缓存命中次数、未命中次数以及新增到缓存中的脏页数。

而 READ_HIT 和 WRITE_HIT ,分别表示读和写的缓存命中率。

工具-指定文件的缓存大小-pcstat

除了缓存的命中率外,还有一个指标你可能也会很感兴趣,那就是指定文件在内存中的缓存
大小。你可以使用 pcstat 这个工具,来查看文件在内存中的缓存大小以及缓存比例。

工具-内存使用的变化趋势查看-vmstat

# 每隔 3 秒输出一组数据
$ vmstat 3
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 6601824 97620 1098784 0 0 0 0 62 322 0 0 100 0 0
0 0 0 6601700 97620 1098788 0 0 0 0 57 251 0 0 100 0 0
0 0 0 6601320 97620 1098788 0 0 0 3 52 306 0 0 100 0 0
0 0 0 6601452 97628 1098788 0 0 0 27 63 326 0 0 100 0 0
2 0 0 6601328 97628 1098788 0 0 0 44 52 299 0 0 100 0 0
0 0 0 6601080 97628 1098792 0 0 0 0 56 285 0 0 100 0 0

从输出中你可以看到,内存的 free 列在不停的变化,并且是下降趋势;而 buffer 和 cache
基本保持不变。

未使用内存在逐渐减小,而 buffer 和 cache 基本不变,这说明,系统中使用的内存一直在
升高。但这并不能说明有内存泄漏
,因为应用程序运行中需要的内存也可能会增大。比如
说,程序中如果用了一个动态增长的数组来缓存计算结果,占用内存自然会增长。

那怎么确定是不是内存泄漏呢?或者换句话说,有没有简单方法找出让内存增长的进程,并
定位增长内存用在哪儿呢?

工具-定位增长内存用在哪儿-memleak

根据前面内容,你应该想到了用 top 或 ps 来观察进程的内存使用情况,然后找出内存使用
一直增长的进程,最后再通过 pmap 查看进程的内存分布。
但这种方法并不太好用,因为要判断内存的变化情况,还需要你写一个脚本,来处理 top
或者 ps 的输出。
这里,我介绍一个专门用来检测内存泄漏的工具,memleak。memleak 可以跟踪系统或
指定进程的内存分配、释放请求,然后定期输出一个未释放内存和相应调用栈的汇总情况
(默认 5 秒)。

当然,memleak 是 bcc 软件包中的一个工具,我们一开始就装好了,执行
/usr/share/bcc/tools/memleak 就可以运行它。比如,我们运行下面的命令:

# -a 表示显示每个内存分配请求的大小以及地址
# -p 指定案例应用的 PID 号
$ /usr/share/bcc/tools/memleak -a -p $(pidof app)
WARNING: Couldn't find .text section in /app
WARNING: BCC can't handle sym look ups for /app
addr = 7f8f704732b0 size = 8192
addr = 7f8f704772d0 size = 8192
addr = 7f8f704712a0 size = 8192
addr = 7f8f704752c0 size = 8192
32768 bytes in 4 allocations from stack
[unknown] [app]
[unknown] [app]
start_thread+0xdb [libpthread-2.27.so]

从 memleak 的输出可以看到,案例应用在不停地分配内存,并且这些分配的地址没有被回
收。
这里有一个问题,Couldn’t find .text section in /app,所以调用栈不能正常输出,最后
的调用栈部分只能看到 [unknown] 的标志。
为什么会有这个错误呢?实际上,这是由于案例应用运行在容器中导致的。memleak 工具
运行在容器之外,并不能直接访问进程路径 /app

简单的方法,就是**在容器外部构建相同路径的文件以及依赖库。**这个案例只有一个二进制文件,所以
只要把案例应用的二进制文件放到 /app 路径中,就可以修复这个问题。
比如,你可以运行下面的命令,把 app 二进制文件从容器中复制出来,然后重新运行
memleak 工具:

$ docker cp app:/app /app
$ /usr/share/bcc/tools/memleak -p $(pidof app) -a
Attaching to pid 12512, Ctrl+C to quit.
[03:00:41] Top 10 stacks with outstanding allocations:
addr = 7f8f70863220 size = 8192
addr = 7f8f70861210 size = 8192
addr = 7f8f7085b1e0 size = 8192
addr = 7f8f7085f200 size = 8192
addr = 7f8f7085d1f0 size = 8192
40960 bytes in 5 allocations from stack
fibonacci+0x1f [app]
child+0x4f [app]
start_thread+0xdb [libpthread-2.27.so]

这一次,我们终于看到了内存分配的调用栈,原来是 fibonacci() 函数分配的内存没释放。
定位了内存泄漏的来源,下一步自然就应该查看源码,想办法修复它。正常修复之后的调用结果如下

# 清理原来的案例应用
$ docker rm -f app
# 运行修复后的应用
$ docker run --name=app -itd feisky/app:mem-leak-fix
# 重新执行 memleak 工具检查内存泄漏情况
$ /usr/share/bcc/tools/memleak -a -p $(pidof app)
Attaching to pid 18808, Ctrl+C to quit.
[10:23:18] Top 10 stacks with outstanding allocations:
[10:23:23] Top 10 stacks with outstanding allocations:
  • 20
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值