背景:

最近,在项目中做性能优化需要监控进程运行时内存。

笔者在​​​​​​Android 查看第三方应用进程的内存开销_xiaobaaidaba123的专栏-CSDN博客_android 内存开销

介绍过使用adb命令查看进程的内存开销。但是如果需要持续监控的话,需要另外的方法。

网上查找了下 使用ActivityManager 中的 getProcessMemoryInfo()方法可以获取到进程运行时内存,但是这个接口在 android P以上 限频了,拿到的内存可能是过去时间的,不能实时呈现运行时内存。而且不同的手机限频的时间不固定。

因此本文介绍的是另外一种方法:通过读取

"/proc/" + android.os.Process.myPid() + "/status" 路径文件里的信息获取。

1. 读文件获取运行时内存(RSS)

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大小来作为内存性能指标。
 

2. 通过接口ActivityManager接口获取运行时内存(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。

3. 通过Runtime接口获取运行时内存(JVM消耗,不包括Native)

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层面的。(如有不对,欢迎各位看官一起指正,讨论)

4. 通过 Debug.getMemoryInfo(Debug.MemoryInfo memoryInfo) 接口。


            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

Logo

智屏生态联盟致力于大屏生态发展,利用大屏快应用技术降低开发者开发、发布大屏应用门槛

更多推荐