
作为Android系统工程师,常年在不同产品项目上会遇到各种内存泄漏,可能是某个应用,可能是framework层的定制化模块,又或者是某个native的进程。那么遇到这样的问题我们有哪些工具可以使用呢?
相关基础知识
后面的章节将涉及到具体的工具,在此之前我们先看看几个基础的概念:
- VSS - Virtual Set Size 虚拟耗用内存(包含共享库占用的内存) RSS - Resident Set Size
- 实际使用物理内存(包含共享库占用的内存) PSS - Proportional Set Size
- 实际使用的物理内存(比例分配共享库占用的内存) USS - Unique Set Size
- 进程独自占用的物理内存(不包含共享库占用的内存)
一般来说内存占用大小有如下规律:VSS >= RSS >= PSS >= USS,如果是看某个进程的独占内存,一般用USS,若要考虑到共享库内存的情况,则可以参考PSS,接下来我们看看具体的工具。
具体的调试工具
procrank
procrank命令可以用来查看当前系统各进程内存(VSS/RSS/PSS/USS)使用快照。
执行命令“procrank”的部分信息如下:

procmem
procmem可以用来查看系统某一个进程的内存(VSS/RSS/PSS/USS)使用情况。可以用ps或者上文介绍的procrank查看进程pid
执行命令“procmem pid”的部分信息如下:

其中:
- ShCl(shared clean): 干净的共享数据, 也就是说, 当前有多个进程的虚拟地址指向该物理空间, 且当前进程空间该数据与disk上一致。
- ShDi(shared dirty) 脏共享数据, 与上面对应, 该进程空间的共享数据与disk上数据不一致。
- PrCl(private clean) 进程私有干净数据, 与disk数据一致。
- PrDi(private dirty) 进程私有脏数据, 与disk数据不一致。
librank
librank可以与procrank互补使用,是procmem的扩展,它可以显示某个库文件或者共享文件真实的内存大小。librank数据来源与procmem一样,但是procmem针对的是单个进程的内存占用情况。 而librank是显示某个库或者文件被哪些进程共享, 并显示占用大小,执行命令“librank”的部分信息如下:

dumpsys meminfo
关于dumpsys命令大家可以查看我们之前的一篇文章:Android系统Debug神器之dumpsys。
dumpsys meminfo可以查看各个进程的PSS占用情况,执行命令“dumpsys meminfo com.gitvdemo.video”的部分信息如下:

cat /proc/meminfo
这条命令主要是帮助我们查看系统内存的详细信息,执行命令后有一些关键字在这里解释一下:
- MemTotal: 系统总的可以用物理内存。
- MemFree: 系统中空闲的物理内存。
- MemAvailable: 系统可用内存= MemFree + Cached + Buffers。
- Buffers: 用于文件缓冲的内存大小。
- Cached: 用于高速缓存器占用的内存大小。
- SwapCached: 用于高速缓存器占用的swap大小。
- Active: 活跃使用中的内存, 一般不会回收。
- Inactive: 最近未被使用的内存, 可以被回收。
- SwapTotal: swap分区大小。
- AnonPages: 匿名页。
- Shmem: 共享内存。
- Slab: 内核 slab分配器。
。。。。。。
等等,还有一些关键字就不一一列举了。
vmstat
vmstat是一个非常不错的监控工具,可以实时提供memory、blockIO和CPU状态信息。它用来对系统整体的情况进行统计,通过它可以快速了解当前系统运行情况及可能碰到的问题。
我们执行命令“vmstat 1 5”时会看到如下信息:

首先解释下命令:
- vmstat后的第一个参数表示每隔1秒重新测量并反馈数据。
- vmstat后的第二个参数表示反馈5次后关闭程序。
查看内存相关的信息我们主要查看memory和swap即可,下面解释下各个参数的含义:
procs
r: Running队列中进程数,这个值超过cpu个数就会出现cpu瓶颈。
b: IO wait 的进程数, 该值过大,可能是等待文件操作的进程多或者文件读写慢。
memory
swpd:使用的虚拟内存大小. 如果大于0,表示手机物理内存不足. 如果不是应用内存泄漏的原因,就是当前后台执行的activity、jobs过多了。
free: 系统可用的物理内存数。
buff: 系统用作buffers的内存数,用来存储比如目录中内容,权限等的缓存。
cache: 系统用作cache的内存数,用来缓存打开的文件或者图片等。
swap
si: 每秒从磁盘(disk)读入的内存大小,如果这个值大于0,表示物理内存不足或者内存泄漏,需要查找耗内存的进程。
so: 每秒从内存写入磁盘的内存大小,如果这个值大于0,表示物理内存不足或者内存泄漏,需要查找耗内存的进程。
io
bi: 块设备接收的块数量,单位blocks/s。
bo: 块设备每秒发送的块数量,单位blocks/s。
system
in: (interrupts) 在delay的时间内(默认为1秒)系统产生的中断数,包括时间中断。
cs: (context switch) 在delay的时间内(默认为1秒)系统上下文切换的次数。
cpu
us: 用户(non-kernelcode)占用的CPU时间比。
sy: 系统时间(kernel code)占用的CPU时间比。
id: Idle 闲置CPU时间比 %,一般情况下id + us + sy = 100%,当然也有可能99%或者101%。
wa: (IO wait) CPU等待IO完成的时间占比。
dumpcache
dumpcache工具是Android7.0之后才有的工具,用于查看文件系统中每个文件被cache到内存的大小,可以用来判断缺页错误时加载的文件。
valgrind
valgrind工具套件包括 Memcheck(用于检测 C 和 C ++ 中与内存相关的错误)、Cachegrind(缓存分析器)、Massif(堆分析器)和其他几种工具。
valgrind对于可退出的进程进行调试比较合适, 可以对应用的native代码进行调试。
在Android系统上使用valgrind需要重新编译:
mmma -j6 external/valgrind
然后将生成的lib/valgrind目录与bin/valgrind文件 push到对应的系统目录下,即可使用。
比如检测test命令是否存在内存泄露问题:
valgrind --tool=memcheck --leak-check=full--error-limit=no --show-reachable=yes test
输出结果的关键字段说明:
definitely lost: 已明确丢失的内存。
indirectly lost: 间接丢失的内存(指向该内存的指针位于内存泄露处)。
still reachable: 指针指向的动态内存还没有被释放就退出了。
possibly lost: 可能丢失的内存。
suppressed: 使用valgrind的某些参数取消了的错误。
注:valgrind有个缺点,它会大大降低程序的运行速度。
其他内存调试手段
- mprotect(c函数):保护指定的内存空间,遇到踩内存的情况可以crash掉程序,让始作俑者无所遁形。
- StricMode(java类):严苛模式检测应用内存泄漏。
- showmap(命令):查看进程虚拟进程空间的内存分配情况和详细的干净脏数据信息。
- AddressSanitizer(编译器自带工具):基于CLang的内存检测工具,可以检测各种内存泄漏、内存越界等问题。
- Android Profiler(Android Studio 3.0): 基于Android Studio 3.0的一款图形化内存泄漏检测工具。
…
写在最后
好了,工具介绍就到这里了,上文提到的这些工具你都用过哪些呢?多用用工具,掌握技巧,想必是可以提高我们debug效率的。工欲善其事必先利其器,工具用得好,很多时候都会事半功倍。
总之系统是复杂的,在不同的场景下灵活使用不同工具(或者工具组合)来定位解决问题是非常有必要的,本文介绍的几款内存调试工具希望对你有些许帮助。
更多精彩内容请关注“麻辣软硬件”公众号,谢谢!

本文介绍了Android系统工程师在面对内存泄漏时可使用的调试工具,包括procrank、procmem、librank、dumpsys meminfo等,详细解析了它们的功能和使用方法,帮助提升内存问题排查效率。

2069

被折叠的 条评论
为什么被折叠?



