在Linux 系统中,我们可以使用free
命令去查看当前系统的一个内存使用情况:
ubuntu@VM-0-2-ubuntu:~/ByteTalk/Porxy$ free
total used free shared buff/cache available
Mem: 3875308 906836 866884 7004 2101588 2684856
Swap: 0 0 0
可以看到,free输出的是一个表格,其中数值都默认以字节为单位。表格总共有两行六列,分别是 Memory 和 Swap 两个的使用情况,每列的数据使用情况如下:
这里我们可以看到,available 比 free 要大很多,这是因为其包含未使用的内存以及可回收的缓存。所以: 新进程可使用内存 = 未使用内存 + 可回收缓存
对于其他几个属性都是字面意思,很容易理解。这个buff 和 cache是干嘛的就很让人迷惑。
man free:
buffers
Memory used by kernel buffers (Buffers in /proc/meminfo)
cache Memory used by the page cache and slabs (Cached and SReclaimable in /proc/meminfo)
buff/cache
Sum of buffers and cache
从手册中,我们可以看到buffer 和 cache的说明:
/proc/meminfo
中的值/proc/meminfo
中的 Cached与SReclaimable之和注:
- Buffers 是对原始磁盘块的临时存储,就是对磁盘数据的缓存,不仅用在读请求中,也会用在写请求中
- Cached 是从磁盘读/写文件时的页缓存。
- SReclaimable 是 Slab 的一部分。Slab 包括两部分,一个是可回收的部分,用SReclaimable记录;而不可回收部分,用SUnreclaim 记录
这里的buffer 和 cache用作缓存的区别是,buffer 用于磁盘块设备文件的缓存,块设备文件不同于普通文件,是Linux 文件系统的载体;而cache 用作的则是文件的缓存,在读写普通文件的时候,I/O请求会首先经过文件系统,然后由文件系统负责,来与磁盘交互。
free 工具显示的是整个系统的内存使用情况。如果你想查看进程的内存使用情况,可以使用 top 或者 ps 等工具。以下是 top 使用的例子:
ubuntu@VM-0-2-ubuntu:~/ByteTalk/Porxy$ top
top - 12:31:21 up 4 days, 13:22, 0 users, load average: 0.00, 0.00, 0.00
Tasks: 128 total, 1 running, 85 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.6 us, 0.6 sy, 0.0 ni, 98.9 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
# total memory:
KiB Mem : 3875308 total, 859736 free, 911556 used, 2104016 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 2680056 avail Mem
# process memory:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
22932 ubuntu 20 0 1522448 270180 31584 S 0.8 7.0 0:13.14 node
1777 root 20 0 985744 41884 14976 S 0.4 1.1 24:21.98 YDService
1873 root 20 0 502424 13252 3056 S 0.4 0.3 16:53.29 barad_agent
20719 root 20 0 0 0 0 I 0.4 0.0 0:00.02 kworker/u4:3
22904 ubuntu 20 0 794364 37724 28516 S 0.4 1.0 0:05.05 node
1 root 20 0 78060 6792 4336 S 0.0 0.2 0:13.39 systemd
在标记处total memory
开始的接下来两行其实也就是在调用free
,显示的是系统总的内存使用情况。
然后从标记处process memory
开始,就是每个进程的内存使用情况,比如:VIRT RES SHR 以及 %MEM:
刚刚我们提到了缓存的这个概念。其利用了SRAM 设备与 ROM设备不同的访问速度来提高进程访问持久化资源的效率。
在评估缓存是否有用的时候,我们就不得不引入缓存命中率这个概念。所谓缓存命中率,是指直接通过缓存获取数据的请求次数,占所有数据请求次数的百分比。
命中率越高,表示使用缓存带来的收益越高,应用程序的性能也就越好。
要使用这两个工具,我们就需要先安装一下:
ubuntu@VM-0-2-ubuntu:~/ByteTalk/Porxy$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4052245BD4284CDD
ubuntu@VM-0-2-ubuntu:~/ByteTalk/Porxy$ echo "deb https://repo.iovisor.org/apt/xenial xenial main" | sudo tee /etc/apt/sources.list.d/iovisor.list
# 更新 apt 仓库
ubuntu@VM-0-2-ubuntu:~/ByteTalk/Porxy$ sudo apt-get update
ubuntu@VM-0-2-ubuntu:~/ByteTalk/Porxy$ sudo apt-get install -y bcc-tools libbcc-examples linux-headers-$(uname -r)
# 添加到系统PATH路径中
ubuntu@VM-0-2-ubuntu:~/ByteTalk/Porxy$ export PATH=$PATH:/usr/share/bcc/tools/
环境配置完成之后,我们运行一下cachestat 和 cachetop命令:
ubuntu@VM-0-2-ubuntu:/usr/share/bcc/tools$ sudo ./cachestat 1 3
HITS MISSES DIRTIES HITRATIO BUFFERS_MB CACHED_MB
42 0 0 100.00% 195 2009
3096 0 17 100.00% 195 2009
1203 0 1 100.00% 195 2009
其中每个属性的含义如下:
我们再来看看cachetop的使用:
ubuntu@VM-0-2-ubuntu:/usr/share/bcc/tools$ sudo ./cachetop
我们一般把程序的虚拟内存分为以下几个部分:堆栈、只读段、数据段以及内存映射段。
其中只有两个地方会发生内存泄漏:堆、内存映射段。
内存泄漏会导致系统没有可用内存,不仅应用程序自己无法访问内存,系统也不能开启其他进程来使用。虽然,系统最终可用通过OOM机制来杀死进程,但是在这之前相当长一段的空白期,可能会导致非常严重的事故,导致严重的性能问题。
我们先用以下代码模拟以下内存泄漏:
void run()
{
for (int i = 0; i < 5; i++)
{
char *p = (char *)malloc(4096);
sleep(1);
}
for(;;) sleep(1);
}
int main()
{
run();
}
然后我们使用vmstat 命令去查看一下当前系统的内存使用情况:
ubuntu@VM-0-2-ubuntu:~/python_file$ 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
6 0 0 309592 232820 2213544 0 0 8 51 9 15 1 1 98 0 0
4 0 0 307772 232820 2213548 0 0 0 0 660 1157 0 0 99 0 0
0 0 0 306448 232820 2213572 0 0 0 24 899 1677 1 1 98 0 0
0 0 0 305860 232820 2213572 0 0 0 35 779 1390 0 1 99 0 0
可以看到,buff 和 cache两列的内容是基本保持不变的,free 列的数据是递减的。这种情况下,貌似是发生了内存泄漏,但是也不敢肯定,因为内存的开辟存在开辟回收的情况,可能是其他进程在申请内存。
所以我们需要使用一个新工具:memleak,其可以跟踪系统或者指定进程的内存分配、释放请求,然后定期食醋胡一个未释放内存和响应调用栈的汇总情况(默认 5秒)。
在命令行使用以下指令:
# -a 表示显示内阁内存分配请求的大小以及地址
# -p 指定案例应用的pid号
# 24483 进程pid号
ubuntu@VM-0-2-ubuntu:~/python_file$ sudo /usr/share/bcc/tools/memleak -a -p 24483
[14:30:41] Top 10 stacks with outstanding allocations:
addr = 56142d125ea0 size = 4096
addr = 56142d12aef0 size = 4096
addr = 56142d127ec0 size = 4096
addr = 56142d126eb0 size = 4096
addr = 56142d129ee0 size = 4096
addr = 56142d128ed0 size = 4096
24576 bytes in 6 allocations from stack
# 这里 run()+0x1f [test]
main+0x13 [test]
__libc_start_main+0xe7 [libc-2.27.so]
[unknown]
可以看到,是test 进程中的run函数造成的内存泄漏。我们接下来只用打开源代码修改就好了。
[1] 倪朋飞.Linux性能优化实战.极客时间