性能分析-内存

要点关注哪些内存指标。如何定位内存相关性能问题。一些常见问题分析。基本概念虚拟内存虚拟内存是主存的抽象,它为每个进程和内核提供了巨大的、线性的、私有的地址空间。它具备三个能力:

要点

  • 关注哪些内存指标。
  • 如何定位内存相关性能问题。
  • 一些常见问题分析。

基本概念

虚拟内存

虚拟内存是主存的抽象,它为每个进程和内核提供了巨大的、线性的、私有的地址空间。它具备三个能力:

1、将主存看成是一个存储在磁盘地址空间的高速缓存,主存中只保存活动区域。

2、为每个进程提供了一致的地址空间,简化了软件开发中,对存储器的管理。

3、保护每个进程的地址空间不被其他进程破坏。

匿名内存

无文件系统位置或路径名的内存,它涉及进程的私有数据:进程堆和栈。

页换出守护进程

页换出守护进程(kswapd),在当空闲内存低于某个阀值时,会被唤醒;当空闲内存高于另一个阀值时会休息。

缺页

DRAM缓存不命中称为缺页,当访问一个尚未从虚拟内存映射到物理内存的页时,会发生缺页。

MMU(内存管理单元)

负责将虚拟内存地址转换为物理内存地址,位于CPU芯片上。它按页做转换,而页内的偏移量则直接映射。

TLB(翻译后备缓冲器)

MMU(存储器管理单元)中对PTE(页表条目)的小缓存,位于CPU芯片上。发生进程上下文切换时,TLB将会进行刷新。

slab内存分配器

内核slab分配器管理特定大小的对象缓存,能被快速地回收利用,避免页分配开销。

内存申请

1、应用程序发起内存申请,如:malloc、realloc。

2、直接从空闲列表中响应请求,或者扩展虚拟内存地址空间再分配:

a.利用brk()系统调用扩展堆的尺寸。

b.利用mmap()系统调用来创建一个新的内存段地址。

3、调用CPU内部的MMU(内存管理单元),将虚拟地址转换为物理地址,若转换失败,则产生缺页错误。

4、内核从物理内存空闲列表中找到一个空闲地址,并映射到该虚拟地址。

5、当系统内存需求超过一定水平时,内核中的页换出守护进程(kswapd)就开始寻找可以释放的内存页:

a.文件系统页:从磁盘读出且没有修改过的页,如,可执行代码、数据等。

b.脏页:发生过修改的页,先写回磁盘后释放。

c.匿名页:如应用程序数据等,先存入换页设备后释放。

文件系统缓存

  • 页缓存:缓存的内容是虚拟内存页,包括文件内容、I/O缓冲信息,目的在于提高文件性能和目录I/O性能。
  • inode缓存:inode是文件系统用于描述所存对象的一个数据结构体。
  • 目录缓存(dcache):包括目录元素名到VFS inode之间的映射信息,提高路径名查找速度。

buffer

为磁盘读写操作提供缓存,如:dd if=/dev/vda1 。在man free 中的解释为"Memory used by kernel buffers (Buffers in /proc/meminfo)"。buffer表示内核缓冲区用到的内存。

cache

为文件读写提供缓存,如:dd if=/tmp/file 。在man free 中的解释为"Memory used by the page cache and slabs (Cached and Slab in /proc/meminfo)"。cache表示内核页缓存和Slab用到的内存。

swap

swap使得系统可用内存变大,将进程暂时不使用的内存数据,存入磁盘,称为换出;将磁盘上的数据载入内存,称为换入。

RSS(常驻集合)

已分配主存大小。

(N)UMA:(非)均匀访存模型

UMA是指,每个CPU通过共享系统总线,访问内存,所有CPU的内存访问延时较均匀;而NUMA,每个CPU其对应的内存节点,当访问其他CPU的内存节点时,需要通过CPU互联发起。因此,本地内存访问要比远程访问延时低。

分析工具

free

系统空闲和已使用内存总量。

# free -h              total        used        free      shared  buff/cache   availableMem:            62G         13G        779M        3.2G         48G         44GSwap:          8.0G        5.6G        2.4G

其中,

total:系统内存总量。

used:已经使用的内存量。

free:未使用的内存量。

shared:tmpfs使用的内存。

buff/cache:buffers和cache总和。

avaiable:在不进行交换的情况下,估计可用于启动新应用程序的内存大小。

vmstat

虚拟内存统计信息,包括当前内存和换页在内的系统内存健康程度。

# vmstat 1procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st 3  0      0 121088 185124 1397420    0    0     3    35    3    3  1  1 98  0  0 0  0      0 120700 185124 1397452    0    0     0   100  465  987  1  1 98  0  0 0  0      0 120716 185124 1397452    0    0     0     0  392  855  0  1 99  0  0 1  0      0 120732 185124 1397452    0    0     0     0  380  832  0  1 99  0  0 0  0      0 120832 185124 1397452    0    0     0     0  490 1041  1  1 98  0  0 0  0      0 120824 185124 1397452    0    0     0     0  426  927  1  1 98  0  0 0  0      0 120840 185124 1397452    0    0     0     0  442  945  1  1 98  0  0 0  0      0 120808 185124 1397456    0    0     0     0  407  937  0  0 100  0  0

swpd:交换出的内存量。

free:空闲的可用内存。

buff:用于缓冲缓存的内存。

cache:用于页缓存的内存。

si:换入的内存。

so:换出的内存。

sar

查看内存相关的历史统计数据。

# sar -Br 1 10Linux 3.10.0-957.el7.x86_64 (localhost.localdomain)   10/29/2021  _x86_64_  (40 CPU)02:45:23 PM  pgpgin/s pgpgout/s   fault/s  majflt/s  pgfree/s pgscank/s pgscand/s pgsteal/s    %vmeff02:45:24 PM      0.00     44.00  14406.00      0.00   5787.00      0.00      0.00      0.00      0.0002:45:23 PM kbmemfree kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive   kbinact   kbdirty02:45:24 PM  13430580  18803276     58.33    566212  13082464  11630772     28.63  13024104   4105728       78402:45:24 PM  pgpgin/s pgpgout/s   fault/s  majflt/s  pgfree/s pgscank/s pgscand/s pgsteal/s    %vmeff02:45:25 PM      0.00    160.00  35787.00      0.00   5878.00      0.00      0.00      0.00      0.0002:45:24 PM kbmemfree kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive   kbinact   kbdirty02:45:25 PM  13429252  18804604     58.34    566212  13082512  11631300     28.63  13024400   4105728       820

内存统计相关的信息选项:

  • -B:换页统计信息。
  • -H:大页面统计信息。
  • -r:内存使用率。
  • -R:内存统计信息。
  • -S:交换空间统计信息。
  • -W:交换统计信息。

相关字段说明:

  • pgpgin/s:页面换入。
  • pgpgout/s:页面换出。
  • fault/s:严重及轻微缺页。
  • majflt/s:严重缺页。
  • pgfree/s:页面加入空闲链表。
  • pgscank/s:被后台页面换出守护进程扫描过的页面(kswapd)。
  • pgscand/s:直接页面扫描。
  • pgsteal/s:页面及交换高速缓存回收。
  • %vmeff:页面回收的效率(pgsteal/pgscan),高数值表示,几乎所有的非活动页面都进行了回收(健康),低数值表示,系统在挣扎中。100%为高数值,少于30%是低数值。未扫描到任何页面则显示为0。

pmap

显示进程的内存映射,显示它们的大小、权限及映射对象。

# pmap -x 36713671:   ./a.outAddress           Kbytes     RSS   Dirty Mode  Mapping0000000000400000       4       4       0 r-x-- a.out0000000000600000       4       4       4 r---- a.out0000000000601000       4       4       4 rw--- a.out0000000002165000     132       4       4 rw---   [ anon ]00007fda646cd000    1808     356       0 r-x-- libc-2.17.so00007fda64891000    2044       0       0 ----- libc-2.17.so00007fda64a90000      16      16      16 r---- libc-2.17.so00007fda64a94000       8       8       8 rw--- libc-2.17.so00007fda64a96000      20      12      12 rw---   [ anon ]00007fda64a9b000      84      12       0 r-x-- libgcc_s-4.8.5-20150702.so.100007fda64ab0000    2044       0       0 ----- libgcc_s-4.8.5-20150702.so.100007fda64caf000       4       4       4 r---- libgcc_s-4.8.5-20150702.so.100007fda64cb0000       4       4       4 rw--- libgcc_s-4.8.5-20150702.so.100007fda64cb1000    1028      64       0 r-x-- libm-2.17.so00007fda64db2000    2044       0       0 ----- libm-2.17.so00007fda64fb1000       4       4       4 r---- libm-2.17.so00007fda64fb2000       4       4       4 rw--- libm-2.17.so00007fda64fb3000     932     472       0 r-x-- libstdc++.so.6.0.1900007fda6509c000    2048       0       0 ----- libstdc++.so.6.0.1900007fda6529c000      32      32      32 r---- libstdc++.so.6.0.1900007fda652a4000       8       8       8 rw--- libstdc++.so.6.0.1900007fda652a6000      84      16      16 rw---   [ anon ]00007fda652bb000     136     108       0 r-x-- ld-2.17.so00007fda654cc000      20      20      20 rw---   [ anon ]00007fda654da000       8       8       8 rw---   [ anon ]00007fda654dc000       4       4       4 r---- ld-2.17.so00007fda654dd000       4       4       4 rw--- ld-2.17.so00007fda654de000       4       4       4 rw---   [ anon ]00007ffd7efe7000     132      16      16 rw---   [ stack ]00007ffd7f1b3000       8       4       0 r-x--   [ anon ]ffffffffff600000       4       0       0 r-x--   [ anon ]---------------- ------- ------- -------total kB           12680    1196     176

使用该命令可以找出内存瓶颈位于哪一部分。

slabtop

通过slab分配器输出内核slab缓存使用情况。

 Active / Total Objects (% used)    : 5107972 / 5815486 (87.8%) Active / Total Slabs (% used)      : 139879 / 139879 (100.0%) Active / Total Caches (% used)     : 76 / 104 (73.1%) Active / Total Size (% used)       : 949466.02K / 1146425.82K (82.8%) Minimum / Average / Maximum Object : 0.01K / 0.20K / 12.75K  OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME292125 170878  58%    1.01K   9427   31    301664K ext4_inode_cache2869269 2697899  94%    0.10K  73571     39    294284K buffer_head975828 974260  99%    0.19K  23234   42    185872K dentry184471 136016  73%    0.57K   6592   28    105472K radix_tree_node393728 110166  27%    0.06K   6152   64     24608K kmalloc-64 35133  33483  95%    0.64K    717   49     22944K proc_inode_cache 11440  11352  99%    2.00K    715   16     22880K kmalloc-2048 30745  30391  98%    0.58K    559   55     17888K inode_cache 93534  93534 100%    0.12K   2751   34     11004K kernfs_node_cache 16422  15846  96%    0.62K    322   51     10304K sock_inode_cache 10048   9913  98%    1.00K    314   32     10048K kmalloc-1024120904 119686  98%    0.07K   2159   56  8636K avc_node  1876   1805  96%    4.06K    268        7  8576K task_struct 12480  11804  94%    0.66K    260   48  8320K shmem_inode_cache 16576  14770  89%    0.50K    518   32  8288K kmalloc-512  4016   3732  92%    1.94K    251   16  8032K TCP  2728   2463  90%    2.69K    248   11  7936K task_xstate 37170  30499  82%    0.19K    885   42  7080K kmalloc-192  2790   2631  94%    2.06K    186   15  5952K sighand_cache121720  82094  67%    0.05K   1432   85  5728K shared_policy_node  4984   4793  96%    1.12K    178   28  5696K signal_cache 25456  24276  95%    0.21K    688   37  5504K vm_area_struct  8256   8180  99%    0.66K    172   48  5504K ovl_inode  2565   2432  94%    2.06K    171   15  5472K TCPv6 20512  18454  89%    0.25K    641   32  5128K kmalloc-256 10512  10271  97%    0.44K    292   36  4672K ip6_dst_cache  4350   4320  99%    1.06K    145   30  4640K UDP 11298  11298 100%    0.38K    269   42  4304K mnt_cache  1000    943  94%    4.00K    125        8  4000K kmalloc-4096 93942  77108  82%    0.04K    921  102  3684K ext4_extent_status   432    384  88%    8.00K    108        4  3456K kmalloc-8192 84048  84048 100%    0.04K    824  102  3296K selinux_inode_security  1335   1335 100%    2.06K     89   15  2848K idr_layer_cache 19584  18929  96%    0.12K    612   32  2448K kmalloc-128  1976   1924  97%    1.19K     76   26  2432K RAWv6 35904  35264  98%    0.06K    561   64  2244K ext4_free_data

输出包括顶部的汇总和slab列表,其中包括对象数据量(OBJS)、多少是活动的(ACTIVE)、使用百分比(USE)、对象大小(OBJ SIZE,字节)和缓存大小(CACHE SIZE,字节)。

dmesg

显示内核ring buffer内容,可以用于诊断设备故障。

valgrind

用于调试或分析程序的工具集。经常用来分析程序是否存在内存泄露。

# valgrind --tool=memcheck ./a.out==13932== Memcheck, a memory error detector==13932== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.==13932== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info==13932== Command: ./a.out==13932==^C==13932====13932== Process terminating with default action of signal 2 (SIGINT)==13932==    at 0x571B8D0: __nanosleep_nocancel (in /usr/lib64/libc-2.17.so)==13932==    by 0x574C1C3: usleep (in /usr/lib64/libc-2.17.so)==13932==    by 0x400893: main (mcount.cpp:24)==13932====13932== HEAP SUMMARY:==13932==     in use at exit: 9,772 bytes in 9,772 blocks==13932==   total heap usage: 9,772 allocs, 0 frees, 9,772 bytes allocated==13932====13932== LEAK SUMMARY:==13932==    definitely lost: 9,772 bytes in 9,772 blocks==13932==    indirectly lost: 0 bytes in 0 blocks==13932==      possibly lost: 0 bytes in 0 blocks==13932==    still reachable: 0 bytes in 0 blocks==13932==         suppressed: 0 bytes in 0 blocks==13932== Rerun with --leak-check=full to see details of leaked memory==13932====13932== For counts of detected and suppressed errors, rerun with: -v==13932== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

使用memcheck工具,可以查看该程序是否存在内存问题。不过,使用valgrind进行内存检测时,对服务的性能影响较大,一般不用直接在线上服务上使用。

cachestat

BCC工具集,显示整个系统的缓存命中情况。

# ./cachestat 1 5    HITS   MISSES  DIRTIES HITRATIO   BUFFERS_MB  CACHED_MB    6688        1     1409   99.99%          553      12820    6597        0     1406  100.00%          553      12820    6495        0     1368  100.00%          553      12820    6712        0     1450  100.00%          553      12820    6748        0     1441  100.00%          553      12820

cachetop

BBC工具集,显示每个进程读写的page cache命中情况。

16:36:57 Buffers MB: 181 / Cached MB: 1208 / Sort: HITS / Order: descendingPID      UID      CMD              HITS     MISSES   DIRTIES  READ_HIT%  WRITE_HIT%   17020 root     lsblk                 391        0        0     100.0%       0.0%   17011 root     sh                    305        0        0     100.0%       0.0%   17012 root     sh                    301        0        0     100.0%       0.0%   17019 root     sh                    298        0        0     100.0%       0.0%   17021 root     awk                   276        0        0     100.0%       0.0%   17014 root     awk                   260        0        0     100.0%       0.0%   17011 root     cat                   146        0        0     100.0%       0.0%   17013 root     cat                   146        0        0     100.0%       0.0%   17019 root     barad_agent           137        0        0     100.0%       0.0%   17020 root     sh                    106        0        0     100.0%       0.0%   17021 root     sh                    103        0        0     100.0%       0.0%

memleak

BBC工具集,用于定位内存泄露问题。

# ./memleak -p 21311Attaching to pid 21311, Ctrl+C to quit.[17:00:13] Top 10 stacks with outstanding allocations:  27566 bytes in 27566 allocations from stack    operator new(unsigned long)+0x1d [libstdc++.so.6.0.19]    main+0x9 [a.out]    __libc_start_main+0xf5 [libc-2.17.so][17:00:19] Top 10 stacks with outstanding allocations:  56396 bytes in 56396 allocations from stack    operator new(unsigned long)+0x1d [libstdc++.so.6.0.19]    main+0x9 [a.out]    __libc_start_main+0xf5 [libc-2.17.so]

从执行结果,可以看出,该程序存在内存泄露的堆栈。

分析策略

1、检查系统信息中是否有OOM Killer杀掉进程的信息:dmesg。

2、检查系统中是否配置了换页设备,以及使用的换页空间大小;并且检查这些换页设备是否有活跃的I/O操作:iostat、vmstat。

3、检查系统中空闲内存的数量,以及整个系统的缓存使用情况:free。

4、按进程检查内存用量:top、ps。

5、检查系统中缺页错误的发生频率,并且检查缺页错误发生时的调用栈信息,这可以解释RSS增长的原因。

6、检查缺页错误和哪些文件有关。

7、跟踪brk()和mmap()调用来从另一个角度审查内存用量。

8、使用PMC测量硬件缓存命空率和内存访问,分析导致内存I/O发生的函数和指令信息:perf。

常见问题

swap活跃

目前的服务器上内存基本充足,因此,大多数服务器上,会将swap关闭。这样可以避免在非必要时刻,触发系统换入换出,从而引起性能问题。

涉及swap的常用操作:

调整swap活跃性:修改/proc/sys/vm/swappiness,取值范围为0~100,值越大越活跃。

关闭swap:swapoff。

开启swap:swapon。

内存泄露

如果条件允许,可以使用valgrind来进行检测,从而定位到存在内存泄露的代码。但是,很多时候,是线上服务发现了内存泄露,不允许随意重启。此时,可以考虑使用memleak来进行跟踪。需要注意的是,无论是valgrind,还是memleak,对服务的性能会有一定的影响。另外,还可以使用pmap找出内存泄漏的内存段,然后,dump出该内存段的内容分析。也可以从代码管理的角度,分析最近的更新记录,找出可能的泄露点。

造成内存泄露的本质原因是,内存申请后未释放。这种未释放,可能是显式调用malloc申请了一块内存,使用完之后,未调用free释放。也可能是,一个常驻内存的全局变量,一直未释放,比如,有一个全局的vector对象,一直在往里面放数据,而从未清空,也会造成内存泄露。

在自己管理内存时,应当遵循"谁申请、谁释放"的原则,使用RAII(资源获取即初始化)的方式进行内存管理,在一定程度上,这样可以避免内存泄漏问题。

core dump

程序出现core dump,通常情况下,是由于进程访问了自身以外的内存空间,或者是访问到了只读地址。出现core dump时,通常的定位方法有:

1、通过gdb调试core文件,查看进程的堆栈,找出问题。

2、如果堆栈已经破坏,确认下是否可以重建堆栈。重建堆栈的原理是,old $RBP中,保存着上一个调用的$RBP地址,同时,8(%RBP)中,保存着old $EIP,通过重置寄存器的值,可以看到一部分调用堆栈。

3、从代码管理角度出发,找出最近的更新记录,分析运行日志,定位问题。

参考

《Systems Performance:Enterprise and Cloud》

《BPF Performance Tools》

《Computer Systems》

《Modern Operating Systems》

http://www.brendangregg.com/linuxperf.html

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/81739.html

(0)

相关推荐