Android | 内存指标与分析方法

2021-01-24  本文已影响0人  彭旭锐

点赞关注,不再迷路,你的支持对我意义重大!

🔥 Hi,我是丑丑。本文 「Android 路线」| 导读 —— 从零到无穷大 已收录,这里有 Android 进阶成长路线笔记 & 博客,欢迎跟着彭丑丑一起成长。(联系方式在 GitHub)


目录


前置知识

这篇文章的内容会涉及以下前置 / 相关知识,贴心的我都帮你准备好了,请享用~

这篇文章偏底层,难免有写错的地方还请你多多斧正哦~


1. 内存类型

Android 系统包括三种不同类型的内存:RAM、zRAM 和 ROM:


2. 内存分页

对于内核来说,无论是内核进程还是用户进程,说到底都是task_struct结构体的一个实例。task_struct 也叫进程描述符(process descriptor),里面记录了进程相关的所有信息。

在 task_struct 中有一个mm_struct的数据结构,也叫内存描述符(memory descriptor),里面记录了 Linux 进程内存管理的所有信息。mm_struct 定义在linux/mm_types.h头文件中,其中有一个页(page)的数据结构:

struct page {
       page_flags_t flags;            标志位(每一位单独表示一种状态)
       atomic_t _count;               引用计数(-1 表示未被使用)
       atomic_t _mapcount;            映射计数
       unsigned long private;         私有数据指针
       struct address_space *mapping; 该页所在地址空间描述结构指针,用于内容为文件的页帧
       pgoff_t index;                 该页描述结构在地址空间 radix 树 page_tree 中的对象索引号即页号
       struct list_head lru;          最近最久未使用 struct slab 结构指针链表头变量
       void *virtual;                 页在虚拟地址中的地址,对于不能映射到内核空间的内存,该值为 NULL
};

—— 图片引用自网络

页(Page)是 Linux 内核进行内存管理的基本单位,通常一个页的大小为 4 KB。根据页面是否使用分为 “可用页” 和 “已使用页” 两种,其中已使用页可以分为以下类别:

2.1 缓存页

缓存页是指有存储器中的文件支持的内存,分为两种:私有页 & 共享页

2.2 匿名页

匿名页是没有存储器中的文件支持的内存(例如由设置了MAP_ANONYMOUS标志的mmap()进行分配)


3. 最大堆内存

为了避免应用滥用内存,Android 系统会限制应用可以申请的最大堆内存,超过此限制就会抛出 OOM 异常。Android 设备出厂后,最大堆内存就已经确定,相关的配置位于系统根目录/system/build.prop文件中,我们可以通过命令查看:

命令:
adb shell cat /system/build.prop
------------------------------------------------------------------
输出:
...
dalvik.vm.heapstartsize=16m           [进程启动的初始堆内存]
dalvik.vm.heapgrowthlimit=128m        [进程最大堆内存]
dalvik.vm.heapsize=192m               [进程最大堆内存(开启 largeHeap="true")]
dalvik.vm.heaptargetutilization=0.75
dalvik.vm.heapminfree=512k
dalvik.vm.heapmaxfree=8m
...
虚拟机参数 描述
dalvik.vm.heapstartsize 进程启动的初始堆内存
dalvik.vm.heapgrowthlimit 进程最大堆内存
dalvik.vm.heapsize 进程最大堆内存(开启 largeHeap="true")
dalvik.vm.heaptargetutilization
dalvik.vm.heapminfree
dalvik.vm.heapmaxfree

在 App 虚拟机启动时,会读取/system/build.prop文件的配置,源码位于:AndroidRuntime.cpp

-> 虚拟机启动
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote) {
    ...
    parseRuntimeOption("dalvik.vm.heapstartsize", heapstartsizeOptsBuf, "-Xms", "4m");
    parseRuntimeOption("dalvik.vm.heapsize", heapsizeOptsBuf, "-Xmx", "16m");
    parseRuntimeOption("dalvik.vm.heapgrowthlimit", heapgrowthlimitOptsBuf, "-XX:HeapGrowthLimit=");
    parseRuntimeOption("dalvik.vm.heapminfree", heapminfreeOptsBuf, "-XX:HeapMinFree=");
    parseRuntimeOption("dalvik.vm.heapmaxfree", heapmaxfreeOptsBuf, "-XX:HeapMaxFree=");
    parseRuntimeOption("dalvik.vm.heaptargetutilization", heaptargetutilizationOptsBuf, "-XX:HeapTargetUtilization=");
    ...
}

需要注意的是,配置dalvik.vm.heapgrowthlimit限制的仅仅是 Java 堆内存,本地内存不受其限制的。换句话说,应用可以使用的最大内存其实是可以大于最大堆内存的。


4. 进程到底占用了多少内存?

在确定进程占用了多少内存时,必须考虑多个进程共享页的情况。在 Linux 里,一个进程占用的内存有四种指标,分别是:

内存指标 全称 描述
VSS Virtual Set Size 一个进程可访问的地址空间
RSS Resident Set Size 常驻内存大小(包含共享页内存)
PSS Proportional Set Size 按比例分摊的内存大小(按比例分摊共享页内存)
USS Unique Set Size 独占内存大小(不包含共享页的内存)

一般来说内存占用大小有如下规律:VSS >= RSS >= PSS >= USS。

—— 图片引用自 https://developer.android.google.cn/topic/performance/memory-management Android Developers

—— 图片引用自 https://www.cnblogs.com/sunsky303/p/13494977.html —— sunsky303 著


5. 内存分析命令

关于输出信息的具体分析,建议直接看 Gityuan 的这篇文章:《Android 内存分析命令》,已经写得非常详细了。

命令 作用
dumpsys meminfo 查看进程的内存使用情况
procrank 查看进程的内存使用情况(输出更详细的 VSS / RSS / PSS / USS 内存指标)
cat/proc/meminfo 查看更加详细的内存信息
free 查看可用内存
showmap 查看虚拟地址区域的内存情况
vmstat 查看内存情况,还可以查看进程运行队列、系统切换、CPU 时间占比等情况

6. 内存分析工具


参考资料


创作不易,你的「三连」是丑丑最大的动力,我们下次见!

上一篇 下一篇

猜你喜欢

热点阅读