最近,在项目中做性能优化需要监控进程运行时内存。
笔者在Android 查看第三方应用进程的内存开销_xiaobaaidaba123的专栏-CSDN博客_android 内存开销
介绍过使用adb命令查看进程的内存开销。但是如果需要持续监控的话,需要另外的方法。
网上查找了下 使用ActivityManager 中的 getProcessMemoryInfo()方法可以获取到进程运行时内存,但是这个接口在 android P以上 限频了,拿到的内存可能是过去时间的,不能实时呈现运行时内存。而且不同的手机限频的时间不固定。
因此本文介绍的是另外一种方法:通过读取
"/proc/" + android.os.Process.myPid() + "/status" 路径文件里的信息获取。
public static long getProcessRealMemory() {
String memFilePath = "/proc/" + android.os.Process.myPid() + "/status";
BufferedReader bufferedReader = null;
try {
FileInputStream fileInputStream = new FileInputStream(memFilePath);
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8");
bufferedReader = new BufferedReader(inputStreamReader);
String line;
while ((line = bufferedReader.readLine()) != null) {
Log.d(TAG, " read line : " + line);
if(!TextUtils.isEmpty(line) && line.contains("VmRSS")) {
String rss = line.split(":")[1].trim().split( " ")[0];
return Integer.parseInt(rss) * 1024;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(bufferedReader != null) {
try{
bufferedReader.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
return -1;
}
优点:暂时没有权限限制,实时获取
缺点:这种读文件的方法拿到的内存是RSS。我们通常期望获取的是PSS
RSS和PSS 的区别:
RSS - Resident Set Size 实际使用物理内存(包含共享库占用的内存)
PSS - Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)假如有3个进程使用同一个共享库,那么每个进程的PSS就包括了1/3大小的共享库内存。通常我们使用PSS大小来作为内存性能指标。
ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
final Debug.MemoryInfo[] memInfo = activityManager.getProcessMemoryInfo(new int[]{android.os.Process.myPid()});
final int totalPss = memInfo[0].getTotalPss();
优点:获取的是PSS
缺点:安卓P以上限制频率,需要隔约5分钟(不同手机间隔不同)才能获取到新的值。而且获取的 PSS 不包括 Graphics。
long totalRuntimeMem = Runtime.getRuntime().totalMemory();
long freeRuntimeMem = Runtime.getRuntime().freeMemory();
long useRuntimeMem = totalRuntimeMem - freeRuntimeMem;
Runtime.getRuntime().totalMemory() 返回的是当前进程的java虚拟机从操作系统申请的总内存
Runtime.getRuntime().freeMemory() 返回的是当前进程的java虚拟机从操作系统申请的总内存中尚未使用的内存大小
那么运行时内存呢?笔者认为是totalMemory()-freeMemory()。代表当前进程的java虚拟机从操作系统申请的总内存中已经消耗的内存大小。
但是计算得到的值 是不包含native内存的,因为笔者发现Runtime计算得到的值 约等于PSS中的Java Heap的值,所以Runtime接口获取的运行时内存只是java层面的。(如有不对,欢迎各位看官一起指正,讨论)
Debug.MemoryInfo memoryInfo = new Debug.MemoryInfo();
Debug.getMemoryInfo(memoryInfo);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String javaHeap = memoryInfo.getMemoryStat("summary.java-heap");
String nativeHeap = memoryInfo.getMemoryStat("summary.native-heap");
String code = memoryInfo.getMemoryStat("summary.code");
String stack = memoryInfo.getMemoryStat("summary.stack");
String graphics = memoryInfo.getMemoryStat("summary.graphics");
String privateOther = memoryInfo.getMemoryStat("summary.private-other");
String system = memoryInfo.getMemoryStat("summary.system");
String swap = memoryInfo.getMemoryStat("summary.total-swap");
}
优点:获取的是PSS,并且没有限频,可以获取到 PSS 的组成部分
缺点:获取的 PSS 不包括 Graphics。Android 6 以下不能通过 Debug.MemoryInfo.getmemoryStat 接口获取组成部分的占用内存,只能通过反射方法获取。
我们应该用哪种方法来对运行时内存进行监控呢?
业界的 APM 工具基本用的是方法2 或者方法4,尽管缺少 Graphics,但是能监控应用大体的运行时内存变化。后续笔者也会调研如何获取 Graphics 部分的内存。欢迎各位看官一起交流~
Demo效果:
GitHub - mikelhm/MikelProjectDemo: Personal Android Demo