废话不说,直接上干货。
1:代码的方式
代码中使用Debug的getMemoryInfo(Debug.MemoryInfo memoryInfo)或ActivityManager的MemoryInfo[] getProcessMemoryInfo(int[] pids)
ActivityManager mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);Debug.MemoryInfo[] memoryInfoArray = mAm.getProcessMemoryInfo(pids);复制代码
MemoryInfo对象中可以看看详细的内存信息,字段如下: 目前很多内存测试工具都是采用这种方式,如哆啦A梦、GT等。这种方式简单方便
2:dumpsys 命令
adb shell dumpsys meminfo package_name|pid复制代码
一般关注关PSS列的这几个字段:
- Native Heap:JNI层的内存
- Dalvik Heap:Java层的内存分配情况
- Total PSS :应用真正占据的内存大小
一般公司测试都是会采用这种方式获取内存数据的
3:smaps文件
Linux在2.6版本之后有一个proc伪文件,在它下面记录各种信息,其中在proc/pid/smaps记录某个pid的内,smaps记录内存详细的使用情况。使用下面的命令可以读文件的值
cat /proc/pid/smaps复制代码
文件格式如下:
文件字段意义:- 400ca000-400cb000:本段虚拟内存的地址范围
- r-xp :文件权限,r(读)、w(写)、x(执行)、p表示私有,s代表共享,如果不具有哪项权限用"-"代替
- 00000000 :映射文件的偏移量
- b3:11 :文件设备号
- 1345 :被映射到虚拟内存文件的映索节点
- /system/lib/libplddbgutil.so:文件名称
- Size:相应虚拟地址空间的大小
- RSS: 正在使用的物理内存的大小
- Shared_Clean: Rss中和其他进程共享的未使用页数
- Shared_Dirty: Rss和其他进程共享已经使用的页数
- Private_Clean: Rss私有区域未使用的页数
- Private_Dirty: Rss私有区域已经使用的页数
smaps文件一般有啥作用呢,譬如我们通过dumpsys meminfo 获取内存时,发现某一项内存数据异常,想弄清楚数据都是有哪些文件产生,我们就可以通过读取smaps详细排查。
4:源码分析
我们执行dumpsys memInfo 命令后的代码执行流程是怎样的呢,dumpsys命令会根据传进来的参数通过函数checkService来找到具体的service, 然后执行该service的dump方法,最总达到dump service的目的。
那我们传进入的memInfo对应的是哪个service呢,这个我们需要查看ActivityManagerService.Java类,搜索一下memInfo,最后我们发现它对应的service是MemBinder。
我们点击MemBinder类看类内部的实现
方法内部首先会进行android.Manifest.permission.DUMP权限检查,最终调用 dumpApplicationMemoryUsage方法,我们再看dumpApplicationMemoryUsage方法做了哪些操作
这个方法内容比较多,这里我们只显示了它最终调的地方,我们最终发现最后会调用android.os.Debug.java类。
其实会调用android.os.Debug.java类调用了大量的Native方法实现的,android.os.Debug.java对应的native文件是android_os_Debug.cpp,上面的Debug.getMemoryInfo(pid, mi)实际上最后调用了 android_os_Debug.cpp的android_os_Debug_getDirtyPagesPid方法
android_os_Debug_getDirtyPagesPid方法调用了load_maps(int pid, stats_t* stats)方法
load_maps中是通过读取smaps文件内容,到此dumpsys memInfo的实现就非常清晰了。
第一种代码的实现方式,其实最后走的流程和上面的是一样的。
android_os_Debug.cpp地址:
5:内存测试关注的点
1.内存是否持续上涨:进入某项功能内存增长,退出功能时内存是否下降
2.是否频繁的GC:频繁的gc可能是因为你的程序内存碎片导致的,这也是需要优化的一个方向
3.内存的峰值是否在单个应用允许的最大值之内