作者邮箱:2107810343@qq.com
时间:2021/05/1416:39
实现环境:Linux
系统:ubuntu 18.04
CPU:2核
内存大小:4G

如何查看Linux内存的使用情况

显示Linux整体内存使用:free

在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 两个的使用情况,每列的数据使用情况如下:

  • total:总内存大小,博主这里是3875308字节,也就是3GB的空间,正好和进程3GB虚拟空间相对应
  • used:已使用的内存大小,包含共享内存
  • free:未使用内存的内存大小
  • shared:共享内存大小
  • buff/cache:缓存和缓冲区的大小
  • available:新进程可使用的内存大小

这里我们可以看到,available 比 free 要大很多,这是因为其包含未使用的内存以及可回收的缓存。所以: 新进程可使用内存 = 未使用内存 + 可回收缓存

buff 和 cache是什么?

对于其他几个属性都是字面意思,很容易理解。这个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的说明:

  • Buffers 是内存缓冲区用到的内存,对应的是 /proc/meminfo 中的值
  • Cache 是内核页缓存和 Slab 用到的内存,对应的是 /proc/meminfo 中的 Cached与SReclaimable之和

  • Buffers 是对原始磁盘块的临时存储,就是对磁盘数据的缓存,不仅用在读请求中,也会用在写请求中
  • Cached 是从磁盘读/写文件时的页缓存。
  • SReclaimable 是 Slab 的一部分。Slab 包括两部分,一个是可回收的部分,用SReclaimable记录;而不可回收部分,用SUnreclaim 记录

这里的buffer 和 cache用作缓存的区别是,buffer 用于磁盘块设备文件的缓存,块设备文件不同于普通文件,是Linux 文件系统的载体;而cache 用作的则是文件的缓存,在读写普通文件的时候,I/O请求会首先经过文件系统,然后由文件系统负责,来与磁盘交互。

查看进程的内存使用情况:top

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:

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

补:top命令的其他用法

输入top命令,按一下’M’可以快速查看内存占用由高到低的清空:

在这里插入图片描述
输入’P’则是可以查看CPU占用的由高到低:
在这里插入图片描述
或者我们可以按组合键’xb’,然后通过<>来手动选择排序的列

理解缓存在内存性能优化扮演的角色

刚刚我们提到了缓存的这个概念。其利用了SRAM 设备与 ROM设备不同的访问速度来提高进程访问持久化资源的效率。

在评估缓存是否有用的时候,我们就不得不引入缓存命中率这个概念。所谓缓存命中率,是指直接通过缓存获取数据的请求次数,占所有数据请求次数的百分比

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

查看系统缓存命中情况:cachestat 和 cachetop

  • cachestat:提供了整个操作系统缓存的读写命中情况
  • cachetop:提供了每个进程的缓存命中情况

要使用这两个工具,我们就需要先安装一下:

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

其中每个属性的含义如下:

  • HITS:表示缓存命中的次数
  • MISSES:表示缓存未命中的次数
  • DIRTIES:表示新增到缓存中的脏页数
  • HITRATIO:缓存命中率
  • BUFFERS_MB:表示buffers 的大小,以MB为单位
  • CACHE_MB:表示Cache的大小,以MB为单位

我们再来看看cachetop的使用:

ubuntu@VM-0-2-ubuntu:/usr/share/bcc/tools$ sudo ./cachetop

在这里插入图片描述
这里我们就可以精确到每个进程了。

内存泄漏

内存泄漏起因

我们一般把程序的虚拟内存分为以下几个部分:堆栈、只读段、数据段以及内存映射段

其中只有两个地方会发生内存泄漏:堆、内存映射段

  • :堆内存由应用程序自己来分配和管理。除非程序退出,这些堆内存并不会被系统自动释放,而是需要库函数去调用free()自己释放,如果没有释放,就会造成内存泄漏
  • 内存映射段:包括动态链接库和共享内存,其中共享内存由程序动态分配和管理。所以,在程序分配后忘了回收,就会导致跟堆内存类似的泄漏问题。

内存泄漏会导致系统没有可用内存,不仅应用程序自己无法访问内存,系统也不能开启其他进程来使用。虽然,系统最终可用通过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性能优化实战.极客时间
[2] 罗剑锋.罗剑锋的C++实战笔记.极客时间
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐