要点关注哪些内存指标。如何定位内存相关性能问题。一些常见问题分析。基本概念虚拟内存 虚拟内存是主存的抽象,它为每个进程和内核提供了巨大的、线性的、私有的地址空间。它具备三个能力: 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。匿名页:如应用程序数据等,先存入换页设备后释放。文件系统缓存页缓存:缓存的内容是虚拟内存页,包括文件内容、IO缓冲信息,目的在于提高文件性能和目录IO性能。inode缓存:inode是文件系统用于描述所存对象的一个数据结构体。目录缓存(dcache):包括目录元素名到VFSinode之间的映射信息,提高路径名查找速度。buffer 为磁盘读写操作提供缓存,如:ddifdevvda1。在manfree中的解释为Memoryusedbykernelbuffers(Buffersinprocmeminfo)。buffer表示内核缓冲区用到的内存。cache 为文件读写提供缓存,如:ddiftmpfile。在manfree中的解释为Memoryusedbythepagecacheandslabs(CachedandSlabinprocmeminfo)。cache表示内核页缓存和Slab用到的内存。swap swap使得系统可用内存变大,将进程暂时不使用的内存数据,存入磁盘,称为换出;将磁盘上的数据载入内存,称为换入。RSS(常驻集合) 已分配主存大小。(N)UMA:(非)均匀访存模型 UMA是指,每个CPU通过共享系统总线,访问内存,所有CPU的内存访问延时较均匀;而NUMA,每个CPU其对应的内存节点,当访问其他CPU的内存节点时,需要通过CPU互联发起。因此,本地内存访问要比远程访问延时低。分析工具free 系统空闲和已使用内存总量。freehtotalusedfreesharedbuffcacheavailableMem:62G13G779M3。2G48G44GSwap:8。0G5。6G2。4G 其中, total:系统内存总量。 used:已经使用的内存量。 free:未使用的内存量。 shared:tmpfs使用的内存。 buffcache:buffers和cache总和。 avaiable:在不进行交换的情况下,估计可用于启动新应用程序的内存大小。vmstat 虚拟内存统计信息,包括当前内存和换页在内的系统内存健康程度。vmstat1procsmemoryswapiosystemcpurbswpdfreebuffcachesisobiboincsussyidwast30012108818512413974200033533119800000120700185124139745200010046598711980000012071618512413974520000392855019900100120732185124139745200003808320199000001208321851241397452000049010411198000001208241851241397452000042692711980000012084018512413974520000442945119800000120808185124139745600004079370010000 swpd:交换出的内存量。 free:空闲的可用内存。 buff:用于缓冲缓存的内存。 cache:用于页缓存的内存。 si:换入的内存。 so:换出的内存。sar 查看内存相关的历史统计数据。sarBr110Linux3。10。0957。el7。x8664(localhost。localdomain)10292021x8664(40CPU)02:45:23PMpgpginspgpgoutsfaultsmajfltspgfreespgscankspgscandspgstealsvmeff02:45:24PM0。0044。0014406。000。005787。000。000。000。000。0002:45:23PMkbmemfreekbmemusedmemusedkbbufferskbcachedkbcommitcommitkbactivekbinactkbdirty02:45:24PM134305801880327658。33566212130824641163077228。6313024104410572878402:45:24PMpgpginspgpgoutsfaultsmajfltspgfreespgscankspgscandspgstealsvmeff02:45:25PM0。00160。0035787。000。005878。000。000。000。000。0002:45:24PMkbmemfreekbmemusedmemusedkbbufferskbcachedkbcommitcommitkbactivekbinactkbdirty02:45:25PM134292521880460458。34566212130825121163130028。63130244004105728820 内存统计相关的信息选项:B:换页统计信息。H:大页面统计信息。r:内存使用率。R:内存统计信息。S:交换空间统计信息。W:交换统计信息。 相关字段说明:pgpgins:页面换入。pgpgouts:页面换出。faults:严重及轻微缺页。majflts:严重缺页。pgfrees:页面加入空闲链表。pgscanks:被后台页面换出守护进程扫描过的页面(kswapd)。pgscands:直接页面扫描。pgsteals:页面及交换高速缓存回收。vmeff:页面回收的效率(pgstealpgscan),高数值表示,几乎所有的非活动页面都进行了回收(健康),低数值表示,系统在挣扎中。100为高数值,少于30是低数值。未扫描到任何页面则显示为0。pmap 显示进程的内存映射,显示它们的大小、权限及映射对象。pmapx36713671:。a。outAddressKbytesRSSDirtyModeMapping0000000000400000440rxa。out0000000000600000444ra。out0000000000601000444rwa。out000000000216500013244rw〔anon〕00007fda646cd00018083560rxlibc2。17。so00007fda64891000204400libc2。17。so00007fda64a90000161616rlibc2。17。so00007fda64a94000888rwlibc2。17。so00007fda64a96000201212rw〔anon〕00007fda64a9b00084120rxlibgccs4。8。520150702。so。100007fda64ab0000204400libgccs4。8。520150702。so。100007fda64caf000444rlibgccs4。8。520150702。so。100007fda64cb0000444rwlibgccs4。8。520150702。so。100007fda64cb10001028640rxlibm2。17。so00007fda64db2000204400libm2。17。so00007fda64fb1000444rlibm2。17。so00007fda64fb2000444rwlibm2。17。so00007fda64fb30009324720rxlibstdc。so。6。0。1900007fda6509c000204800libstdc。so。6。0。1900007fda6529c000323232rlibstdc。so。6。0。1900007fda652a4000888rwlibstdc。so。6。0。1900007fda652a6000841616rw〔anon〕00007fda652bb0001361080rxld2。17。so00007fda654cc000202020rw〔anon〕00007fda654da000888rw〔anon〕00007fda654dc000444rld2。17。so00007fda654dd000444rwld2。17。so00007fda654de000444rw〔anon〕00007ffd7efe70001321616rw〔stack〕00007ffd7f1b3000840rx〔anon〕ffffffffff600000400rx〔anon〕totalkB126801196176 使用该命令可以找出内存瓶颈位于哪一部分。slabtop 通过slab分配器输出内核slab缓存使用情况。ActiveTotalObjects(used):51079725815486(87。8)ActiveTotalSlabs(used):139879139879(100。0)ActiveTotalCaches(used):76104(73。1)ActiveTotalSize(used):949466。02K1146425。82K(82。8)MinimumAverageMaximumObject:0。01K0。20K12。75KOBJSACTIVEUSEOBJSIZESLABSOBJSLABCACHESIZENAME292125170878581。01K942731301664Kext4inodecache28692692697899940。10K7357139294284Kbufferhead975828974260990。19K2323442185872Kdentry184471136016730。57K659228105472Kradixtreenode393728110166270。06K61526424608Kkmalloc643513333483950。64K7174922944Kprocinodecache1144011352992。00K7151622880Kkmalloc20483074530391980。58K5595517888Kinodecache93534935341000。12K27513411004Kkernfsnodecache1642215846960。62K3225110304Ksockinodecache100489913981。00K3143210048Kkmalloc1024120904119686980。07K2159568636Kavcnode18761805964。06K26878576Ktaskstruct1248011804940。66K260488320Kshmeminodecache1657614770890。50K518328288Kkmalloc51240163732921。94K251168032KTCP27282463902。69K248117936Ktaskxstate3717030499820。19K885427080Kkmalloc19227902631942。06K186155952Ksighandcache12172082094670。05K1432855728Ksharedpolicynode49844793961。12K178285696Ksignalcache2545624276950。21K688375504Kvmareastruct82568180990。66K172485504Kovlinode25652432942。06K171155472KTCPv62051218454890。25K641325128Kkmalloc2561051210271970。44K292364672Kip6dstcache43504320991。06K145304640KUDP11298112981000。38K269424304Kmntcache1000943944。00K12584000Kkmalloc40969394277108820。04K9211023684Kext4extentstatus432384888。00K10843456Kkmalloc819284048840481000。04K8241023296Kselinuxinodesecurity133513351002。06K89152848Kidrlayercache1958418929960。12K612322448Kkmalloc12819761924971。19K76262432KRAWv63590435264980。06K561642244Kext4freedata 输出包括顶部的汇总和slab列表,其中包括对象数据量(OBJS)、多少是活动的(ACTIVE)、使用百分比(USE)、对象大小(OBJSIZE,字节)和缓存大小(CACHESIZE,字节)。dmesg 显示内核ringbuffer内容,可以用于诊断设备故障。valgrind 用于调试或分析程序的工具集。经常用来分析程序是否存在内存泄露。valgrindtoolmemcheck。a。out13932Memcheck,amemoryerrordetector13932Copyright(C)20022017,andGNUGPLd,byJulianSewardetal。13932UsingValgrind3。13。0andLibVEX;rerunwithhforcopyrightinfo13932Command:。a。out13932C1393213932Processterminatingwithdefaultactionofsignal2(SIGINT)13932at0x571B8D0:nanosleepnocancel(inusrlib64libc2。17。so)13932by0x574C1C3:usleep(inusrlib64libc2。17。so)13932by0x400893:main(mcount。cpp:24)1393213932HEAPSUMMARY:13932inuseatexit:9,772bytesin9,772blocks13932totalheapusage:9,772allocs,0frees,9,772bytesallocated1393213932LEAKSUMMARY:13932definitelylost:9,772bytesin9,772blocks13932indirectlylost:0bytesin0blocks13932possiblylost:0bytesin0blocks13932stillreachable:0bytesin0blocks13932suppressed:0bytesin0blocks13932Rerunwithleakcheckfulltoseedetailsofleakedmemory1393213932Forcountsofdetectedandsuppressederrors,rerunwith:v13932ERRORSUMMARY:0errorsfrom0contexts(suppressed:0from0) 使用memcheck工具,可以查看该程序是否存在内存问题。不过,使用valgrind进行内存检测时,对服务的性能影响较大,一般不用直接在线上服务上使用。cachestat BCC工具集,显示整个系统的缓存命中情况。。cachestat15HITSMISSESDIRTIESHITRATIOBUFFERSMBCACHEDMB66881140999。9955312820659701406100。0055312820649501368100。0055312820671201450100。0055312820674801441100。0055312820cachetop BBC工具集,显示每个进程读写的pagecache命中情况。16:36:57BuffersMB:181CachedMB:1208Sort:HITSOrder:descendingPIDUIDCMDHITSMISSESDIRTIESREADHITWRITEHIT17020rootlsblk39100100。00。017011rootsh30500100。00。017012rootsh30100100。00。017019rootsh29800100。00。017021rootawk27600100。00。017014rootawk26000100。00。017011rootcat14600100。00。017013rootcat14600100。00。017019rootbaradagent13700100。00。017020rootsh10600100。00。017021rootsh10300100。00。0memleak BBC工具集,用于定位内存泄露问题。。memleakp21311Attachingtopid21311,CtrlCtoquit。〔17:00:13〕Top10stackswithoutstandingallocations:27566bytesin27566allocationsfromstackoperatornew(unsignedlong)0x1d〔libstdc。so。6。0。19〕main0x9〔a。out〕libcstartmain0xf5〔libc2。17。so〕〔17:00:19〕Top10stackswithoutstandingallocations:56396bytesin56396allocationsfromstackoperatornew(unsignedlong)0x1d〔libstdc。so。6。0。19〕main0x9〔a。out〕libcstartmain0xf5〔libc2。17。so〕 从执行结果,可以看出,该程序存在内存泄露的堆栈。分析策略 1、检查系统信息中是否有OOMKiller杀掉进程的信息:dmesg。 2、检查系统中是否配置了换页设备,以及使用的换页空间大小;并且检查这些换页设备是否有活跃的IO操作:iostat、vmstat。 3、检查系统中空闲内存的数量,以及整个系统的缓存使用情况:free。 4、按进程检查内存用量:top、ps。 5、检查系统中缺页错误的发生频率,并且检查缺页错误发生时的调用栈信息,这可以解释RSS增长的原因。 6、检查缺页错误和哪些文件有关。 7、跟踪brk()和mmap()调用来从另一个角度审查内存用量。 8、使用PMC测量硬件缓存命空率和内存访问,分析导致内存IO发生的函数和指令信息:perf。常见问题swap活跃 目前的服务器上内存基本充足,因此,大多数服务器上,会将swap关闭。这样可以避免在非必要时刻,触发系统换入换出,从而引起性能问题。 涉及swap的常用操作: 调整swap活跃性:修改procsysvmswappiness,取值范围为0100,值越大越活跃。 关闭swap:swapoff。 开启swap:swapon。内存泄露 如果条件允许,可以使用valgrind来进行检测,从而定位到存在内存泄露的代码。但是,很多时候,是线上服务发现了内存泄露,不允许随意重启。此时,可以考虑使用memleak来进行跟踪。需要注意的是,无论是valgrind,还是memleak,对服务的性能会有一定的影响。另外,还可以使用pmap找出内存泄漏的内存段,然后,dump出该内存段的内容分析。也可以从代码管理的角度,分析最近的更新记录,找出可能的泄露点。 造成内存泄露的本质原因是,内存申请后未释放。这种未释放,可能是显式调用malloc申请了一块内存,使用完之后,未调用free释放。也可能是,一个常驻内存的全局变量,一直未释放,比如,有一个全局的vector对象,一直在往里面放数据,而从未清空,也会造成内存泄露。 在自己管理内存时,应当遵循谁申请、谁释放的原则,使用RAII(资源获取即初始化)的方式进行内存管理,在一定程度上,这样可以避免内存泄漏问题。coredump 程序出现coredump,通常情况下,是由于进程访问了自身以外的内存空间,或者是访问到了只读地址。出现coredump时,通常的定位方法有: 1、通过gdb调试core文件,查看进程的堆栈,找出问题。 2、如果堆栈已经破坏,确认下是否可以重建堆栈。重建堆栈的原理是,oldRBP中,保存着上一个调用的RBP地址,同时,8(RBP)中,保存着oldEIP,通过重置寄存器的值,可以看到一部分调用堆栈。 3、从代码管理角度出发,找出最近的更新记录,分析运行日志,定位问题。参考 《SystemsPerformance:EnterpriseandCloud》 《BPFPerformanceTools》 《ComputerSystems》 《ModernOperatingSystems》 http:www。brendangregg。comlinuxperf。html