Android 系统中查看进程的物理内存真实占用的方法
Android 系统中查看进程的物理内存真实占用的方法
Qidi Huang 2025.08.21
0. 工具使用
很多人都使用过 top
命令查看进程的内存占用情况。不过这种方式看到的 RES 内存占用可能重复计算了某些内存单元,或者包含了未映射到物理内存的虚拟内存区,因此并不能反应进程对物理内存的真实占用情况。
Android 系统提供一款专有工具 procrank
用于查看各进程(包括 Java 进程)的内存占用情况。由于该工具需要读取 /proc/<pid>/pagemap
文件信息,所以必须在 root
权限下运行。
使用方法很简单,不带参数直接运行即可:
b07-1:/ # procrank
通过 -h
参数可查看使用说明:
b07-1:/ # procrank -h
Usage: procrank [ -W ] [ -v | -r | -p | -u | -s | -h ]
-v Sort by VSS.
-r Sort by RSS.
-p Sort by PSS.
-u Sort by USS.
-s Sort by swap.
(Default sort order is PSS.)
-R Reverse sort order (default is descending).
-c Only show cached (storage backed) pages
-C Only show non-cached (ram/swap backed) pages
-k Only show pages collapsed by KSM
-w Display statistics for working set only.
-W Reset working set of all processes.
-o Show and sort by oom score against lowmemorykiller thresholds.
-h Display this help screen.
如果想在 Linux 系统中使用 procrank
,可以移植 Github 上的项目:https://github.com/csimmonds/procrank_linux。此项目基于 Android 5.1 代码修改。
当然,也可以自己动手试试基于新版 Android 代码移植。以 Android 16 为例,procrank
源码位于 /system/memory/libmeminfo/tools
目录下。
1. 输出内容
procrank
命令会输出所有进程的内存占用信息列表。不带参数运行时,默认对各进程按照 PSS
内存占用大小降序排列。 因为内存使用的最小单位是 页(page),一个内存页是 4KB,所以列表中除 PSS
内存外,其余内存占用数值均为 4 的倍数。
输出列表如下:
b07-1:/ # procrank
PID Vss Rss Pss Uss Swap PSwap USwap ZSwap cmdline
1872 17056640K 671036K 565470K 563248K 0K 0K 0K 0K com.ts.kanziservice
2025 18142732K 516280K 405013K 400852K 0K 0K 0K 0K com.gwm.speech.daemon
2928 18009208K 309212K 207403K 205116K 0K 0K 0K 0K com.gwm.speech.adapter
1746 16652912K 282308K 159536K 152168K 0K 0K 0K 0K com.gwm.app.launcher
1296 18379008K 258992K 120278K 110200K 0K 0K 0K 0K system_server
5256 16401728K 191788K 78816K 74952K 0K 0K 0K 0K com.gwm.app.hvac
1611 16958312K 199764K 76404K 71300K 0K 0K 0K 0K com.android.systemui
4207 16482132K 172696K 71110K 68764K 0K 0K 0K 0K com.gwm.app.map
2976 16652896K 184916K 65943K 59292K 0K 0K 0K 0K com.gwm.app.applet
3334 16959676K 174652K 64850K 61468K 0K 0K 0K 0K com.gwm.app.media.qqmusic
260 12484796K 64680K 62120K 62084K 0K 0K 0K 0K /system/bin/logd
4451 16409280K 186676K 60787K 51524K 0K 0K 0K 0K com.beantechs.stemcells:plugin1
3379 16442296K 179936K 55545K 46048K 0K 0K 0K 0K com.beantechs.stemcells
2951 16176688K 141696K 43078K 41460K 0K 0K 0K 0K com.gwm.app.account.service
2641 16120252K 140540K 39273K 37164K 0K 0K 0K 0K com.android.providers.media.module
523 1815104K 120924K 38283K 12924K 0K 0K 0K 0K zygote
5554 16075948K 131208K 36142K 34580K 0K 0K 0K 0K com.gwm.app.smartmanager:remote
2605 16622544K 152824K 35342K 31720K 0K 0K 0K 0K com.gdc.carplay
4929 16247064K 154264K 34422K 25660K 0K 0K 0K 0K com.beantechs.stemcells:plugin8
1584 17628348K 137316K 32764K 30500K 0K 0K 0K 0K com.android.car
786 12701296K 48328K 32485K 30280K 0K 0K 0K 0K /system/bin/surfaceflinger
3640 16193644K 142668K 32385K 29056K 0K 0K 0K 0K com.gwm.app.vrassistant
1944 16839736K 126672K 28701K 26768K 0K 0K 0K 0K com.gwm.android.adapter.server
2683 16380376K 128488K 26969K 24796K 0K 0K 0K 0K com.beantechs.linkfusion
271 92984K 29520K 26153K 25128K 0K 0K 0K 0K /vendor/bin/hw/android.hardware.audio.service
......
输出列表里各字段含义如下:
-
Vss
Virtual Set Size,进程的虚拟内存占用大小。有时也被叫作 VSZ 或 VIRT,比如使用ps
命令可以显示 VSZ,使用top
命令可以显示 VIRT。即使进程只是声明了一块虚拟内存却没有使用,也会被视为占用了这部分内存,计入 Vss。 比如,进程声明了一片内存malloc(7000)
,且只使用了前 100 bytes,那么这 100 bytes 实际占用的物理内存大小为 4KB(至少分配一页内存),但 Vss 依然是按照 7000 bytes 计算,也就是两页,即 8KB。 -
Rss
Resident Set Size,进程的最大物理内存占用大小。有时也被叫作 RES,比如使用top
命令可以显示 RES。如果一个动态库同时被多个进程使用,那么每个进程的 Rss 都会将这个动态库的完整大小计入在内。 实际情况下,由于总是有动态库被多个进程使用,所以系统里所有进程的 Rss 值相加起来一定大于系统的实际物理内存使用值,因为动态库的内存占用被重复计算了。 -
Pss
Proportional Set Size,进程的实际物理内存占用大小(字面直译为 按比例计算的内存占用大小)。如果一个动态库的大小为 m KB,同时被 n 个进程使用,那么只有m/n
KB 内存会被计入每个进程的 Pss。 系统里所有进程的 Pss 值相加起来等于系统的实际物理内存使用值。 进程的 Pss 值可以较为准确地反映其真实占用的物理内存大小。 -
Uss
Unique Set Size,进程的独占内存大小。只统计进程的私有内存大小。如果一个动态库同时被多个进程使用,那么每个进程的 Uss 都不会将这个动态库的大小计入在内。 如果该进程终止运行,那么只会有 Uss 大小的物理内存会得到释放(被多个进程共享的动态库,即便当前进程终止,由于其它进程仍在运行,所以其占用的内存不会被释放)。 -
Swap
磁盘交换分区占用大小。当系统内存不足时,会将内存中使用最不频繁的数据暂时换出到磁盘交换分区,以释放部分物理内存。所以,当系统内存充足时,Swap 的值为 0。通过这个值可以判断系统内存不足的程度。相应的,当系统内存不足时,通过top
命令也可以观察到kswapd
进程的 CPU 占用高。 -
PSwap
Proportional Swap,进程的实际磁盘交换分区占用大小(字面直译为 按比例计算的磁盘交换分区大小)。当系统内存充足时,PSwap 的值为 0。 -
USwap
Unique Swap,进程独占的磁盘交换分区占用大小。当系统内存充足时,USwap 的值为 0。 -
ZSwap
Ziped Swap,压缩后的磁盘交换分区占用大小。为了减少写磁盘数据量,可以对要换出的内存先进行压缩、再写入磁盘,以达到缩短 I/O 时间、保护磁盘、提升程序响应速度的目的。典型的 ZSwap 是通过介于内存和磁盘之间的专有缓存区实现的。在嵌入式设备上更常用的类似技术是 ZRam。但 ZRam 是直接对内存进行压缩,压缩后也存放在内存中,而不写入磁盘。所以这种情况下将看到procrank
输出的 ZSwap 字段值始终为 0。 通过查看/proc/swaps
文件可以知道当前设备使用的是常规 Swap、ZSwap 还是 ZRam 技术。比如在我使用的设备上可以看到:
b07-1:/ # cat /proc/swaps
Filename Type Size Used Priority
/dev/block/zram0 partition 1048572 0 32758
从以上字段含义能看出,按大小排序:Uss <= Pss <= Rss <= Vss
2. 补充
procrank
显示的是所有进程的内存占用概要情况。如果要查看某个特定进程的内存占用情况,可以使用 showmap
命令。在输出列表里甚至可以看到这个进程使用了哪些动态库:
b07-1:/ # showmap `pidof android.hardware.audio.service`
virtual shared shared private private
size RSS PSS clean dirty clean dirty swap swapPSS # object
-------- -------- -------- -------- -------- -------- -------- -------- -------- ---- ------------------------------
664 508 71 488 0 0 20 0 0 4 /apex/com.android.runtime/bin/linker
572 572 73 552 0 0 20 0 0 4 /apex/com.android.runtime/lib/bionic/libc.so
12 12 4 8 0 0 4 0 0 3 /apex/com.android.runtime/lib/bionic/libdl.so
12 12 4 8 0 0 4 0 0 3 /apex/com.android.runtime/lib/bionic/libdl_android.so
128 128 32 120 0 0 8 0 0 4 /apex/com.android.runtime/lib/bionic/libm.so
12 8 8 0 0 4 4 0 0 3 /apex/com.android.vndk.v30/lib/android.hardware.audio.common@2.0.so
20 12 12 0 0 8 4 0 0 3 /apex/com.android.vndk.v30/lib/android.hardware.audio.common@5.0.so
20 20 16 8 0 8 4 0 0 3 /apex/com.android.vndk.v30/lib/android.hardware.audio.common@6.0.so
1044 1024 1020 8 0 956 60 0 0 4 /apex/com.android.vndk.v30/lib/android.hardware.audio.effect@6.0.so
700 696 692 8 0 652 36 0 0 4 /apex/com.android.vndk.v30/lib/android.hardware.audio@6.0.so
132 132 128 8 0 112 12 0 0 4 /apex/com.android.vndk.v30/lib/android.hardware.automotive.vehicle@2.0.so
......
68 68 64 8 0 52 8 0 0 4 /apex/com.android.vndk.v30/lib/android.hidl.allocator@1.0.so
64 64 27 52 0 0 12 0 0 4 /apex/com.android.vndk.v30/lib/android.hidl.memory.token@1.0.so
104 104 40 92 0 0 12 0 0 4 /apex/com.android.vndk.v30/lib/android.hidl.memory@1.0.so
12 8 8 0 0 4 4 0 0 3 /apex/com.android.vndk.v30/lib/android.hidl.safe_union@1.0.so
92 92 88 8 0 72 12 0 0 4 /apex/com.android.vndk.v30/lib/android.system.suspend@1.0.so
24 24 20 8 0 8 8 0 0 4 /apex/com.android.vndk.v30/lib/libaudioroute.so
104 100 82 36 0 56 8 0 0 3 /apex/com.android.vndk.v30/lib/libaudioutils.so
112 112 108 8 0 92 12 0 0 4 /apex/com.android.vndk.v30/lib/libbacktrace.so
176 152 55 144 0 0 8 0 0 4 /apex/com.android.vndk.v30/lib/libbase.so
380 360 148 324 0 0 36 0 0 4 /apex/com.android.vndk.v30/lib/libbinder.so
480 480 135 460 0 0 20 0 0 4 /apex/com.android.vndk.v30/lib/libc++.so
56 56 21 44 0 0 12 0 0 4 /apex/com.android.vndk.v30/lib/libcutils.so
100 100 54 88 0 0 12 0 0 3 /apex/com.android.vndk.v30/lib/libexpat.so
20 20 16 8 0 8 4 0 0 3 /apex/com.android.vndk.v30/lib/libfmq.so
12 12 6 8 0 0 4 0 0 3 /apex/com.android.vndk.v30/lib/libhardware.so
......
如果想检查某个文件正在被哪些进程使用,可以使用 lsof
命令。比如查看 libaudioutils.so
被各进程使用的情况:
b07-1:/ # lsof /apex/com.android.vndk.v30/lib/libaudioutils.so
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
audio.service 269 root mem REG 254,0 101456 783 /apex/com.android.vndk.v30/lib/libaudioutils.so
omx@1.0-service 1152 mediacodec mem REG 254,0 101456 783 /apex/com.android.vndk.v30/lib/libaudioutils.so