一、检查meminfo
console:/ # cat /proc/meminfo
MemTotal: 1892784 kB
MemFree: 281508 kB
MemAvailable: 1089928 kB
Buffers: 13740 kB
Cached: 841000 kB
SwapCached: 0 kB
Active: 518860 kB
Inactive: 734844 kB
Active(anon): 401492 kB
Inactive(anon): 4824 kB
Active(file): 117368 kB
Inactive(file): 730020 kB
Unevictable: 256 kB
Mlocked: 256 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 52 kB
Writeback: 0 kB
AnonPages: 399252 kB
Mapped: 257672 kB
Shmem: 7368 kB
Slab: 108576 kB
SReclaimable: 40048 kB
SUnreclaim: 68528 kB
KernelStack: 23232 kB
PageTables: 27188 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 946392 kB
Committed_AS: 56310412 kB
VmallocTotal: 258998208 kB
VmallocUsed: 108496 kB
VmallocChunk: 258743780 kB
Meminfo里面列出来很多项,只需要关注如下几条:
MemTotal: 总内存大小
MemFree: 可以直接使用的空闲的内存大小
MemAvailable: 可以使用的内存,包括MemFree和Cached/Buffers/Slab中可以回收的部分。
Buffers: 直接读写的块设备和文件系统元数据(如super block)所使用的内存。
Cached: 从磁盘读取的文件内容的缓存大小,另外也包含了Shmem内存页
Active(anon):匿名页内存,主要是用户模式下的堆、栈,以及shmem,最近访问过
Inactive(anon):匿名页内存,主要是用户模式下的堆、栈,以及shmem,最近没有访问过
Active(file):基于文件的内存页,包含cache和buffer,但是不包含Shmem内存页,最近访问过
Inactive(file): 基于文件的内存页,包含cache和buffer,但是不包含Shmem内存页,最近没有访问过
AnonPages:匿名页内存,但是不包括Shmem 。所以AnonPages大约等于Active(anon) + Inactive(anon) - Shmem
Mapped: 设备和文件等映射的大小,共享内存、可执行程序的文件、动态库、mmap的文件等都统计在这里
Slab: slab 分配器分配的内存,主要是内核中使用
SReclaimable:slab中可回收的内存。
SUnreclaim:slab中不可回收的内存。
如果机器在运行的过程中MemAvailable持续减小,就可以怀疑系统中存在内存泄漏。但是并不是MemAvailable减小就一定存在内存泄漏,还要进一步分析减少的内存被什么占用了。如果Active(anon)+Inactive(anon)所占用的内存增加,说明用户进程占用的内存增加。如果SUnreclaim所占用的内存增加,说明kernel占用的内存增加。知道了减少的内存是被用户进程还是kernel占用之后,要分析是哪个用户进程或内核哪部分占用了内存。
二、用户进程占用内存增加
用户进程的内存页分为两种:基于文件的内存页和匿名内存页。
Active(file)/ Inactive(file)中的内存是可回收的,当系统内存不足时,系统会将这部分内存释放掉,所以这部分内存不会存在内存泄漏。所以要判断用户进程是否有内存泄漏,我们只需要看Active(anon)/Inactive(anon)是否持续增加就可以了。一旦发现Active(anon)+Inactive(anon)持续增加,我们就可以通过dumpsys meminfo命令来检查进程所占用的内存空间。如果哪个进程有持续的内存泄漏,它占用的内存一定会持续增加,根据这个信息我们就可以找到可疑的进程,再进一步分析。
dumpsys meminfo
Applications Memory Usage (in Kilobytes):
Uptime: 702679 Realtime: 702679
Total PSS by process:
86,379K: system (pid 1375)
84,592K: surfaceflinger (pid 537)
63,558K: com.android.systemui (pid 1613)
56,996K: com.histone.postest (pid 3111 / activities)
29,792K: zygote (pid 576)
29,053K: com.android.launcher3ex (pid 2247 / activities)
25,333K: com.android.phone (pid 1944)
24,179K: com.android.inputmethod.latin (pid 3134)
22,759K: com.android.bluetooth (pid 1787)
21,780K: com.google.android.inputmethod.pinyin (pid 1800)
21,446K: mm-qcamera-daemon (pid 605)
17,518K: com.android.calculator2 (pid 2998 / activities)
17,132K: zygote64 (pid 575)
13,696K: com.android.chrome (pid 2963)
13,238K: com.android.settings (pid 1823)
10,341K: android.process.acore (pid 2131)
9,689K: audioserver (pid 577)
9,300K: com.android.email (pid 2565)
8,793K: rild (pid 592)
8,507K: .dataservices (pid 2173)
8,494K: netmgrd (pid 663)
8,031K: rild (pid 650)
7,223K: ims_rtp_daemon (pid 1116)
6,918K: cnd (pid 556)
5,935K: imscmservice (pid 1118)
5,810K: com.android.calendar (pid 2515)
5,809K: android.process.media (pid 3041)
5,758K: mm-pp-dpps (pid 549)
5,417K: com.android.providers.calendar (pid 2551)
5,267K: com.histone.pos.shareduserfile (pid 2777)
5,223K: qseeproxydaemon (pid 603)
三、内核内存泄漏
内核中主要通过slab来动态分配内存。Slab分配的内存又分为SReclaimable和SUnreclaim,其中SReclaimable是可回收的,SUnreclaim是不可回收的。我们要检查内核中是否有内存泄漏,只需要检查SUnreclaim有没有持续增加即可。一旦发现SUnreclaim持续增加,我们需要检查slabinfo,
CONFIG_STACKTRACE=y
CONFIG_DEBUG_SLAB=y
CONFIG_SLABINFO=y
增大buffer
如上图,我们在海信SC60的slabinfo中发现kmalloc-8192占用的内存持续增加,所以需要对kmalloc-8192做进一步分析。
如上图,这个命令会把当前申请的slab的函数打印出来,同时还会打印出每个函数申请slab的次数,如上图__netdev_alloc_skb申请了192次kmalloc-8192。这里边只列出来当前申请了,还没有被释放的内存次数。我们可以观察系统在运行中,哪个函数申请slab的次数会持续增加,如果这个函数申请slab持续增加,就需要检查这个函数对应代码是否存在内存泄漏。