一、概述
由于app要接入某一厂商,对内存有严格的使用要求,比如初始内存要小于多少M,使用过程最大内存不得超过多少M等条件。对于这些量化的数据,我们需要重哪里获取呢。
在开发阶段,我们除了用Android提供的工具,比如 Android Profiler 、MAT 等工具来通过可视化的方式洞察到我们应用程序的占用内存,Android中也提供了详细命令让我们使用。
二、内存管理概览
Android 运行时 (ART) 和 Dalvik 虚拟机使用分页和内存映射来管理内存。这意味着应用修改的任何内存,无论修改的方式是分配新对象还是轻触内存映射的页面,都会一直驻留在 RAM 中,并且无法换出。要从应用中释放内存,只能释放应用保留的对象引用,使内存可供垃圾回收器回收。
2.1 内存指标概念
- USS 物理内存,进程独占的内存
- PSS 物理内存,PSS= USS+ 按比例包含共享库
- RSS 物理内存 RSS= USS+ 包含共享库
- VSS 虚拟内存 VSS= RSS+ 未分配实际物理内存
故内存的大小关系:VSS >= RSS >= PSS >= USS
2.2 分配与回收应用内存
Dalvik 堆局限于每个应用进程的单个虚拟内存范围。这定义了逻辑堆大小,该大小可以根据需要增长,但不能超过系统为每个应用定义的上限。
堆的逻辑大小与堆使用的物理内存量不同。在检查应用堆时,Android 会计算按比例分摊的内存大小 (PSS) 值,该值同时考虑与其他进程共享的脏页和干净页,但其数量与共享该 RAM 的应用数量成正比。此 (PSS) 总量是系统认为的物理内存占用量
Dalvik 堆不压缩堆的逻辑大小,这意味着 Android 不会对堆进行碎片整理来缩减空间。只有当堆末尾存在未使用的空间时,Android 才能缩减逻辑堆大小。但是,系统仍然可以减少堆使用的物理内存。垃圾回收之后,Dalvik 遍历堆并查找未使用的页面,然后使用 madvise 将这些页面返回给内核。因此,大数据块的配对分配和解除分配应该使所有(或几乎所有)使用的物理内存被回收。但是,从较小分配量中回收内存的效率要低得多,因为用于较小分配量的页面可能仍在与其他尚未释放的数据块共享。
2.3 限制应用内存
为了维持多任务环境的正常运行,Android 会为每个应用的堆大小设置硬性上限。不同设备的确切堆大小上限取决于设备的总体可用 RAM 大小。如果您的应用在达到堆容量上限后尝试分配更多内存,则可能会收到 OutOfMemoryError。
三、内存分析命令
常用的内存调优分析命令:
- dumpsys meminfo
- dumpsys procstats
- cat /proc/meminfo
- free
- vmstat
命令均在 adb shell
使用
3.1 dumpsys meminfo
adb shell dumpsys meminfo
可以查看各个进程使用状况
通过这个命令我们可以一下了解到我们的手机目前占用内存从大到小的排序,com.hugh.basis
这款是我打开的调试app,在app开启后,我们也可通过此命令来看我们应用的初始内存占用多少
PSS是实际使用的物理内存,这是对应用 RAM 占用情况的衡量,考虑了在进程之间共享 RAM 页的情况,PSS 结果一个比较好的特性是,您可以将所有进程的 PSS 相加来确定所有进程正在使用的实际内存。这意味着 PSS 适合测定进程的实际 RAM 比重和比较其他进程的 RAM 使用情况与可用总 RAM。
RAM: 随机存取存储器,也称做手机内存。内存越大,我们可以读写的动态数据就越多,这样手机的反应速度就会越快。
dumpsys meminfo
命令的输出结果分以下4部分:
序列 | 类型 | 排序 | 解释 |
---|---|---|---|
1 | process | PSS | 以进程的PSS从大到小依次排序显示,每行显示一个进程; |
2 | OOM adj | PSS | PSS Native/System/Persistent/Foreground/Visible/Perceptible/A Services/Home/B Services/Cached,分别显示每类的进程情况 |
3 | category | PSS | 以Dalvik/Native/.art mmap/.dex map等划分的各类进程的总PSS情况 |
4 | total | - | 总内存、剩余内存、可用内存、其他内存 |
我们具体看下输出命令
dumpsys meminfo
Applications Memory Usage (in Kilobytes):
Uptime: 2964163 Realtime: 2964163
Total PSS by process: //对应第一部分 以进程的PSS从大到小依次排序显示,每行显示一个进程
193,237K: system (pid 1529)
130,817K: com.miui.home (pid 3080 / activities)
129,076K: com.tencent.mm (pid 4578)
87,370K: com.android.systemui (pid 1948 / activities)
69,517K: surfaceflinger (pid 572)
64,283K: com.hugh.basis (pid 10651 / activities)
...
Total PSS by OOM adjustment://第二部分 以特定的类显示格子内部进程占用大小,比如前台进程,服务进程等
347,695K: Native
70,373K: surfaceflinger (pid 575)
58,815K: logd (pid 447)
17,732K: libweexjsb.so (pid 13870)
...
494,426K: Persistent
244,529K: system (pid 1565)
114,672K: com.android.systemui (pid 1978 / activities)
24,642K: com.android.phone (pid 2158)
...
92,509K: Foreground
60,764K: com.hugh.basis (pid 15218 / activities)
27,542K: com.miui.securitycenter.remote (pid 2830)
4,203K: com.mobiletools.systemhelper:register (pid 5769)
Total PSS by category://第三部分,以Dalvik/Native/.art mmap/.dex map等划分的各类进程的总PSS情况
399,675K: Dalvik
295,839K: Native
124,249K: .oat mmap
82,444K: .dex mmap
77,968K: Dalvik Other
...
//第四部分,整体RAM的使用情况
Total RAM: 2,758,952K (status moderate)
Free RAM: 1,067,004K ( 407,072K cached pss + 616,164K cached kernel + 43,768K free)
Used RAM: 2,329,444K (1,937,064K used pss + 392,380K kernel)
Lost RAM: -388,634K
ZRAM: 125,548K physical used for 559,004K in swap (1,048,572K total swap)
Tuning: 256 (large 512), oom 322,560K, restore limit 107,520K (high-end-gfx)
adb shell dumpsys meminfo package_name|pid [-d]
可以查看各个进程使用状况
如果你想看一个进程更详细内存占用,就可以加上包名或进程名,-d 标记会输出更多与 Dalvik 和 ART 内存占用情况相关的信息。
一般情况下,仅需关注 Pss Total
和 Private Dirty
列
3.2 dumpsys procstats
如果我们想要获取某一时段我们应用的占用内存,包括应用在后台运行的时长以及在该期间内的内存占用情况。它可以帮助您快速找到应用中的低效环节和不当行为(如内存泄漏),这些问题可能会影响应用的表现,特别是在低内存设备上运行时。
比如要获取过去三小时内应用的内存占用情况统计信息
adb shell dumpsys procstats --hours 3
我们可以自定义开始的阶段以及结束的阶段来获取某一个业务场景消耗内存的平均值以及最大值
dumpsys procstats --start-testing
dumpsys procstats 应用包名
dumpsys procstats --stop-testing
我们来看看 dumpsys procstats 应用包名
的输出日志,从下面的示例中可以看出,输出会显示应用运行时间的百分比,以及 PSS、USS 和 RSS(minPSS-avgPSS-maxPSS/minUSS-avgUSS-maxUSS/minRSS-avgRSS-maxRSS over 样本数)。
dumpsys procstats com.hugh.basis
CURRENT STATS:
System memory usage:
SOff/Norm: 1 samples:
Cached: 600MB min, 600MB avg, 600MB max
Free: 39MB min, 39MB avg, 39MB max
ZRam: 178MB min, 178MB avg, 178MB max
Kernel: 389MB min, 389MB avg, 389MB max
Native: 341MB min, 341MB avg, 341MB max
...
Per-Package Stats:
* com.hugh.basis / u0a975 / v1:
* com.hugh.basis / u0a975 / v1:
TOTAL: 21% (57MB-58MB-60MB/53MB-54MB-56MB over 17)
Top: 21% (57MB-58MB-60MB/53MB-54MB-56MB over 17)
Summary:
* com.hugh.basis / u0a975 / v1:
TOTAL: 21% (57MB-58MB-60MB/53MB-54MB-56MB over 17)
Top: 21% (57MB-58MB-60MB/53MB-54MB-56MB over 17)
3.3 cat /proc/meminfo
cat /proc/meminfo
MemTotal: 2758952 kB //RAM可用的总大小
MemFree: 65684 kB //RAM未使用的大小
MemAvailable: 441600 kB
Buffers: 41812 kB //用于文件缓冲
Cached: 518620 kB //用于高速缓存
SwapCached: 7360 kB //用于swap缓存
Active: 821260 kB //活跃使用状态,记录最近使用过的内存,通常不回收用于其它目的
Inactive: 906040 kB //非活跃使用状态,记录最近并没有使用过的内存,能够被回收用于其他目的
Active(anon): 619416 kB
Inactive(anon): 698004 kB
Active(file): 201844 kB
Inactive(file): 208036 kB
Unevictable: 144600 kB
Mlocked: 144600 kB
SwapTotal: 1048572 kB//swap总大小
SwapFree: 496024 kB //swap可用大小
Dirty: 52 kB //等待往磁盘回写的大小
Writeback: 0 kB //正在往磁盘回写的大小
AnonPages: 1305932 kB
Mapped: 297416 kB
Shmem: 6208 kB
Slab: 197964 kB //kernel数据结构的缓存大小,Slab=SReclaimable+SUnreclaim
SReclaimable: 58596 kB //可回收的slab的大小
SUnreclaim: 139368 kB //不可回收slab的大小
KernelStack: 55920 kB
PageTables: 65112 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 2428048 kB
Committed_AS: 112247844 kB
VmallocTotal: 258998208 kB //总分配的虚拟地址空间
VmallocUsed: 272404 kB //已使用的虚拟地址空间
VmallocChunk: 258624484 kB //虚拟地址空间可用的最大连续内存块
对于cache和buffer也是系统可以使用的内存。所以系统总的可用内存为 MemFree+Buffers+Cached
3.4 free
查看可用内存,缺省单位KB。该命令比较简单、轻量,专注于查看剩余内存情况。数据来源于/proc/meminfo。
capricorn:/ $ free
total used free shared buffers
Mem: 2825166848 2775478272 49688576 6356992 43368448
-/+ buffers/cache: 2732109824 93057024
Swap: 1073737728 565772288 507965440
3.5 vmstat
不仅可以查看内存情况,还可以查看进程运行队列、系统切换、CPU时间占比等情况,另外该指令还是周期性地动态输出。
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
r b swpd free buff cache si so bi bo in cs us sy id wa
3 0 552320 47316 42908 535840 12 27 1071 105 2 1386 15 18 67 0
四、小结
dumpsys meminfo
更适用于某一时刻的内存情况,比如我们可以唤醒应用,等应用展现页面,查看应用的初始内存。查看进程的oom adj,或者dalvik/native等区域内存情况,或者某个进程或apk的内存情况,功能非常强大;dumpsys procstats
则适用于查看某一时段的内存使用情况,比如获取某一段业务使用的平均值以及最大值。cat /proc/meminfo
适用场景: 查看系统的详尽内存信息,包含内核情况;free
适用场景: 只查看系统的可用内存;vmstat
适用场景: 周期性地打印出进程运行队列、系统切换、CPU时间占比等情况;