内存管理-31-系统内存统计-6-dumpsys meminfo-1-系统字段
一、系统字段简介
系统部分示例(基于A16+Linux=6.1)
# dumpsys meminfo ... Total RAM: 13,098,184K (status normal) Free RAM: 2,929,753K ( 680,353K cached pss + 1,430,720K cached kernel + 818,680K free) DMA-BUF: 2,365,356K (1,483,508K mapped + 881,848K unmapped) DMA-BUF Heaps: 952,520K DMA-BUF Heaps pool: 637,228K GPU: 2,484,176K (2,484,176K dmabuf + 0K private) Used RAM: 9,881,206K (8,098,762K used pss + 1,782,444K kernel) Lost RAM: 436,233K ZRAM: 23,236K physical used for 58,112K in swap (9,541,628K total swap) Tuning: 512 (large 512), oom 967,680K, restore limit 322,560K (high-end-gfx)
打印入口位置是 ActivityManagerService.java 的 dumpApplicationMemoryUsage(), 约 13090 行起。
dumpsys meminfo 并非只读一个文件:它由三路数据拼接而成:
--------------------------------------------------------------------------------------------- 来源 覆盖字段 --------------------------------------------------------------------------------------------- /proc/meminfo(MemInfoReader.java) Total、cached kernel、free 各进程 PSS 汇总(/proc/<pid>/smaps) cached pss、used pss 驱动 sysfs / BPF / memtrack HAL DMA-BUF、Heaps、pool、GPU、ZRAM 物理量 ---------------------------------------------------------------------------------------------
1. Total RAM
Total RAM: 13,098,184K (status normal)
1.1 公式 = /proc/meminfo 的 MemTotal(MemInfoReader.java 的 getTotalSizeKb())。
13,098,184K 只是 Android 伙伴系统管理的RAM大小。不是物理 DRAM,因为已扣掉 hypervisor/QNX 预留、内核镜像、dtb 保留、qseecom/smmu等。
1.2 status = AppProfiler.dumpLastMemoryLevelLocked(ActivityManagerService.java),取值 normal/moderate/low/critical:有 lowmem detector 时用它上报的 memfactor,否则按 cached+empty 进程数估算(AppProfiler.java:setMemFactorOverrideLocked())。它是压力等级,不是余量。
2. Free RAM
Free RAM: 2,929,753K ( 680,353K cached pss + 1,430,720K cached kernel + 818,680K free)
公式:
Free RAM = cached pss + cached kernel + free = cachedPss + MemInfoReader::getCachedSizeKb() + getFreeSizeKb() //ActivityManagerService.java 中调用 MemInfoReader 对象的成员函数
不能用它判断内存压力:AOSP 注释(ActivityManagerService.java:memInfo.getCachedSizeKb()的调用位置)明确把 ION/DMA-BUF heap pool 视为可回收并计入 cached kernel。判断压力应看 PSI、MemAvailable、水位、lmkd。
2.1 cached pss
公式:
cached pss 680,353K:所有 oomAdj >= CACHED_APP_MIN_ADJ(900) 进程的 PSS 之和(ActivityManagerService.java:dumpApplicationMemoryUsage():12873-12875)。
它是“缓存进程占用”,不是空闲内存,需要杀进程才能腾出来。
2.2 cached kernel
公式:
cached kernel 1,430,720K = Buffers + KReclaimable + Cached − Mapped + SwapCached(MemInfoReader.java:getCachedSizeKb())。
KReclaimable=0 时 KReclaimable 回退为 SReclaimable 的值,定制修改: Mapped ≥ Buffers+KReclaimable+Cached 时直接返回 0。 本机关键修正:本机 exporter 中存在 qcom_hgsl,说明这是 HGSL 方案;这类页通过 vm_insert_page() 直接插入进程页表,会计入 Mapped,但不在 Cached 中,因此 Cached−Mapped 会被过度扣减,导致这个值相对真实可回收量偏低。
2.3 free
公式:
free = MemFree
3. DMA-BUF
DMA-BUF: 2,365,356K (1,483,508K mapped + 881,848K unmapped)
ION 统计不可用,本机走 DMA-BUF 分支。DMA-BUF = mapped + unmapped。
公式:
DMA-BUF 的值取自变量 totalExportedDmabuf = 遍历 /sys/kernel/dmabuf/buffers/*/size 求和,然后除以 1024,按 buffer 去重。
取值来自 Debug.getDmabufTotalExportedKb(Debug.java) --> GetDmabufTotalExportedKb(dmabuf_sysfs_stats.cpp)
表示 DMA BUF 的总大小,包含 heap 部分和非 heap 部分。
(1) 对比:
实测,下面这个累计的值和上面得到的 DMA-BUF 的值相等。
# awk '{s+=$1} END{print s+0}' /sys/kernel/dmabuf/buffers/*/size //只计数 # awk '{s+=$1} END{printf "%.0f KB\n", s/1024}' /sys/kernel/dmabuf/buffers/*/size //单位换算为KB
(2) 统计方法
内核每创建一个 dma-buf,就在 /sys/kernel/dmabuf/buffers/<inode number>/ 下建一个目录,里面有两个文件:exporter_name(谁创建的)、size(字节),导出位置 drivers/dma-buf/dma-buf-sysfs-stats.c。一个 buffer 一个目录,所以按目录遍历天然按 buffer 去重,多进程共享不会重复计数。
3.1 mapped
公式:
mapped = 遍历全部 /proc/<pid>/maps,每个映射引用按 buffer 大小累加。
代码位置: Debug.getDmabufMappedSizeKb(Debug.java) --> android_os_Debug_getDmabufMappedSizeKb(android_os_Debug.cpp) --> ReadDmaBufMapRefs(dmabufinfo.cpp)
这是一个可重复计数的映射引用量,同一 buffer 被 N 个进程映射就计 N 次。
mapped 按“整个 buffer”计一次,不是按照映射区间长度计算,比如一个 4MB buffer 只 map 了 4KB,也按 4MB 计。
3.2 unmapped
公式:
unmapped = totalExportedDmabuf − mapped
没人映射,说明是驱动内部持有,因此被加进 kernelUsed。
4. DMA-BUF Heaps
DMA-BUF Heaps: 952,520K
取值 Debug.getDmabufHeapTotalExportedKb(Debug.java) --> ReadDmabufHeapTotalExportedKb(dmabufinfo.cpp 默认参数定义在 sysmeminfo.h)
表示通过 /dev/dma_heap/* 分配走,还没释放的 buffer 的总量,是 DMA-BUF 总量的子集。它已包含在上面 DMA-BUF 里,不再重复计入 kernelUsed.
数据来源于 dmabuf sysfs + /dev/dma_heap,先按 exporter 聚合,再只取“名字与设备节点名精确相等”的项求和(见 sysmeminfo.cpp)
5. DMA-BUF Heaps pool
DMA-BUF Heaps pool: 637,228K
数据来源 /sys/kernel/dma_heap/total_pools_kb,表示池中空闲页。内核遍历每个已注册 heap,累加 页数×页大小,除以 1024(dma-heap.c:393-407、qcom_system_heap.c:571-583)
已经包含在 KReclaimable → 已在 cached kernel 里,绝不能再加一次。它和 KReclaimable − SReclaimable(misc)同族但采样时刻不同,不必相等。
6. GPU
GPU: 2,484,176K (2,484,176K dmabuf + 0K private)
总量:BPF map /sys/fs/bpf/map_gpuMem_gpu_mem_total_map,key = (gpu_id<<32 | pid),取 pid 0 / gpu 0 为全局(sysmeminfo.cpp:430-477)。
6.1 private
private:memtrack HAL 的 memtrack_proc_gl_pss(pid 0)(android_os_Debug.cpp:707-733);
6.2 dmabuf
dmabuf = 总量 − private。private 计入 kernelUsed(ActivityManagerService.java:13200)。
本快照 private=0 很可能是 HAL stub 没实现,此时 dmabuf 列=总量;GPU 与 DMA-BUF 是两套独立统计,GPU 2.48G > DMA-BUF 2.37G 不矛盾。
7. Used RAM
Used RAM: 9,881,206K (8,098,762K used pss + 1,782,444K kernel)
公式:
Used RAM = used pss + kernel
Used RAM = (PSS_total − cachedPss) + kernelUsed;
7.1 used pss
used pss 是非缓存进程 PSS,不是总 PSS;总 PSS = used pss + cached pss = 8,098,762+680,353 = 8,779,115K
7.2 kernel
公式:
kernel = Shmem + SUnreclaim + VmAllocUsed + PageTables(MemInfoReader.java:122-129),再叠加 dmabufUnmapped(本快照 +881,848K)、gpuPrivate(+0).
kernelUsed 不含 cachedKernel,两者互补不可相加。
8. Lost RAM
Lost RAM: 436,233K
公式:
Lost = Total − (PSS−SwapPSS) − MemFree − cachedKernel + Shmem − kernelUsed − zram
9. ZRAM
ZRAM: 23,236K physical used for 58,112K in swap (9,541,628K total swap)
9.1 physical used
physical used = MemInfoReader.getZramTotalSizeKb() <-- JNI 插入的 "Zram:" 标签(android_os_Debug.cpp:433)<-- libmeminfo 特判:遍历 /sys/block/zramN/mm_stat 取第 3 列 mem_used_total(字节)/1024,
9.2 in swap
in swap = SwapTotal − SwapFree(ActivityManagerService.java::13252-13254),换出页的原始大小。
压缩比: 58,112/23,236 ≈ 2.5:1;物理 23,236K 已在 kernelUsed 里,所以 Lost RAM 末尾要 −zram 抵消。
9.3 total swap
total swap = SwapTotal(zram0 的 disksize)。来自 /proc/swap。
10. Tuning
Tuning: 512 (large 512), oom 967,680K, restore limit 322,560K (high-end-gfx)
512 = staticGetMemoryClass(),读 dalvik.vm.heapgrowthlimit,为空则回退 large(ActivityManager.java:1767-1775)。
large 512 = staticGetLargeMemoryClass(),读 dalvik.vm.heapsize(ActivityManager.java:1795-1800)。 实测:heapgrowthlimit 为空、heapsize=512m → 打印 512 (large 512),与此快照一致。
oom 967,680K = getMemLevel(CACHED_APP_MAX_ADJ=999),返回字节数再 /1024 打印(ActivityManagerService.java:13281、ProcessList.java:1550-1557)。可精确复现:mOomMinFreeHigh[5]=552,960KB × 7/4(64 位加成,ProcessList.java:423-427+:1093,12GB 上 scale 饱和为 1)= 967,680KB = 945MB,即希望为缓存后台保留的空闲水位。
restore limit 322,560K = oom/1024/3(:1118)= 967,680/3 ✓ = 315MB,含义:内存紧张时只有 PSS ≤315MB 的 cached 进程才允许恢复。
(high-end-gfx) = isHighEndGfx()(ActivityManager.java:1838-1843):非 low-ram 且未禁用硬件加速;若 ro.config.low_ram 为真会改印 (low-ram)。
二、相关补充
1. dalvik.vm.heapgrowthlimit 介绍
这个属性是普通 App 进程的 Java 堆上限。名字带 dalvik 是历史原因,ART 时代也沿用这个属性。核心作用是限制单个 App 进程能用到的最大 Dalvik/ART Heap,超过就抛 OutOfMemoryError。对应 Java 层 API:
ActivityManager.getMemoryClass() //单位 MB,基本就是这个值
Runtime.getRuntime().maxMemory() //同一限制的字节数
(1) 相关属性 dalvik.vm.heapsize:
dalvik.vm.heapgrowthlimit 是默认上限。所有 App 默认都受它限制。而 dalvik.vm.heapsize 是 largeHeap 上限。只有在 AndroidManifest.xml 里声明了 " <application android:largeHeap="true"> " 的 App,才能用到这个更大的值,对应 getLargeMemoryClass()。厂商会根据 RAM 大小设置,例如 2GB RAM 手机可能是 128m/512m,8GB+ 手机可能是 256m/512m。
工作原理: App 进程从 Zygote fork 出来时会读这个值初始化虚拟机堆:堆从 dalvik.vm.heapstartsize(如 8m)开始。根据 heaptargetutilization, heapminfree, heapmaxfree 动态扩容/ GC。但永远不能超过 heapgrowthlimit(或 largeHeap 下的 heapsize)。所以它不是物理内存占用限制,只限 Java 堆,不含 Native/Graphics/ mmap 等。
开发者意义:这个值决定了你做图片缓存、内存缓存时能用多大,不能写死,要动态按 getMemoryClass() 来算。
(2) 实测 /system/build.prop 是 heapsize=36m,而 /vendor/build.prop 是 512m,getprop 返回 512m —— vendor 覆盖 system。所以新增属性建议写在 vendor 那一块,与 heapsize 同处:PRODUCT_PROPERTY_OVERRIDES += dalvik.vm.heapgrowthlimit=256m
posted on 2026-11-02 15:20 Hello-World3 阅读(1) 评论(0) 收藏 举报
浙公网安备 33010602011771号