内存管理-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)    收藏  举报

导航