内存管理-31-系统内存统计-6-dumpsys meminfo-2-理论实现


一、dumpsys meminfo <pid> 的实现原理

下面基于你当前两套源码说明;Android 12 与 Android 14 的核心链路基本一致, 主要基于 Android 12 来说。


1. 整体调用链

adb shell dumpsys meminfo <pid>
        │
        ▼
dumpsys 客户端查找 Binder 服务 "meminfo"  //binder域中的有名服务
        │
        ▼
IBinder::dump(fd, args)
        │
        ▼
system_server / ActivityManagerService.MemBinder
        │
        ▼
AMS 根据 PID 查找 ProcessRecord
        │
        ├─ Java/Android 应用进程
        │    ├─ system_server 读取 /proc/<pid>/smaps
        │    ├─ 调用 memtrack 获取 GPU/GL 内存
        │    └─ Binder 调用目标进程 IApplicationThread.dumpMemInfo()
        │          ├─ 获取 Native Heap allocator 信息
        │          ├─ 执行一次 Runtime.gc()
        │          ├─ 获取 ART/Dalvik Heap 信息
        │          ├─ 统计 View/Activity/Binder/SQLite 等对象
        │          └─ 将结果写入 TransferPipe
        │
        └─ Native 进程
             ├─ system_server 读取 /proc/<pid>/smaps
             ├─ 调用 memtrack
             └─ system_server 本地格式化输出

因此,dumpsys meminfo <pid> 不是简单地打印 /proc/<pid>/status,而是将三类数据组合起来:

(1) 内核 /proc/<pid>/smaps;
(2) graphics memtrack HAL/驱动统计;
(3) 目标 Android 应用进程内部的 ART、malloc、对象和 SQLite 统计。


2. dumpsys 命令如何进入 meminfo 服务

2.1 dumpsys 查找 Binder 服务

实现位于:android\frameworks\native\cmds\dumpsys\dumpsys.cpp,dumpsys meminfo <pid> 中:meminfo 是 Binder service 名称;<pid> 是传给该服务的参数。

sp<IBinder> service = sm_->checkService(serviceName);

int sfd[2];
pipe(sfd);

activeThread_ = std::thread([=, remote_end{std::move(remote_end)}]() mutable {
    err = service->dump(remote_end.get(), args);
});

工作方式是:

(1) 从 ServiceManager 查找 meminfo;
(2) 创建 pipe;
(3) 后台线程调用 IBinder::dump();
(4) Binder 服务把文本写到 pipe 写端;
(5) dumpsys 从读端转发到终端;
(6) dumpsys 自己负责超时控制。

所以这里没有直接读取进程内存,真正采集都在 system_server 中进行。


3. meminfo Binder 服务注册

服务由 AMS 注册:

//android\frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java
ServiceManager.addService("meminfo", new MemBinder(this), false, DUMP_FLAG_PRIORITY_HIGH);

Binder 实现是 ActivityManagerService.MemBinder,约 1965 行。

其 dump() 中主要做三件事:

mCachedAppOptimizer.enableFreezer(false);

DumpUtils.checkDumpAndUsageStatsPermission(...);

PriorityDump.dump(mPriorityDumper, fd, pw, args);

mCachedAppOptimizer.enableFreezer(true);

含义:
(1) 临时关闭 cached app freezer,避免目标进程被冻结后无法响应 Binder;
(2) 检查 DUMP/usage stats 权限;
(3) 调用:dumpApplicationMemoryUsage(...)
(4) 完成后重新允许 freezer。

这也是为什么 shell/root 能执行,而普通应用不能随意读取所有进程的详细 meminfo。


4. 参数解析和进程选择

入口位于:android\frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java

4.1 支持的主要参数包括:

---------------------------------------------------------------------
参数             作用
---------------------------------------------------------------------
-a               完整信息、完整分类、Dalvik 子分类和 SwapPss
-d               显示 Dalvik/ART 详细分类
-s               只显示 App Summary
-S               显示 SwapPss
--local          只在 system_server 本地采集,不调用目标进程
--unreachable    扫描 native unreachable memory
--oom            按 oom_adj 分组显示
--package        参数按 package 解析
--checkin        输出机器可解析的 CSV 格式
--proto          输出 protobuf
---------------------------------------------------------------------

随后调用:collectProcesses(...) 这个 Java 函数把 PID、进程名或包名转换为 ProcessRecord。


4.2 如果只指定一个 Android 应用 PID:

procs.size() == 1

AMS 会自动设置:opts.dumpDetails = true; 所以普通的:adb shell dumpsys meminfo <pid> 对 Java 应用来说,实际属于详细采集路径,不只是快速读取 PSS。


5. Java 应用进程的两阶段采集

单个应用进程主要分为两个阶段。


5.1 阶段 1:system_server 读取目标 PID 的 smaps

AMS 中:

//android\frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java
Debug.MemoryInfo mi = new Debug.MemoryInfo();

if (!Debug.getMemoryInfo(pid, mi)) {
    continue;
}

这里发生在 system_server 中,它直接读取目标进程的:/proc/<pid>/smaps,并把结果填入 Debug.MemoryInfo。因此不需要目标应用主动配合,就能拿到:

PSS;
RSS;
Private Dirty/Clean;
Shared Dirty/Clean;
Swap;
SwapPss;
各种 VMA 分类;
memtrack graphics 数据。


5.2 阶段 2:调用目标进程补充运行时数据

如果不是 --local,AMS 随后调用:thread.dumpMemInfo(...), 这里的 thread 是目标应用的 IApplicationThread Binder 接口。AMS 使用 TransferPipe:

TransferPipe tp = new TransferPipe();

thread.dumpMemInfo(tp.getWriteFd(), mi, ...);
tp.go(fd, 5000);

普通 dump 超时约 5 秒;--unreachable 路径超时约 30 秒。

注意 Debug.MemoryInfo mi 已经由 system_server 填好,然后通过 Binder parcel 传给目标应用。目标应用不再重新读取 smaps,主要负责补充其内部运行时数据并格式化输出。


6. 目标应用中的 ActivityThread.dumpMemInfo()

实现位于:android\frameworks\base\core\java\android\app\ActivityThread.java(约 1357 行)

目标进程执行:ApplicationThread.dumpMemInfo(...), 然后进入私有 dumpMemInfo()。


6.1 Native Heap 统计
long nativeMax = Debug.getNativeHeapSize() / 1024;
long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
long nativeFree = Debug.getNativeHeapFreeSize() / 1024;

这是 malloc allocator 视角的数据:

Heap Size;
Heap Alloc;
Heap Free。

它们不是 PSS/RSS,而是 allocator 管理的逻辑 heap 数据。


6.2 主动执行一次 GC

源码明确调用:

Runtime runtime = Runtime.getRuntime();
runtime.gc();

随后才统计:

long dalvikMax = runtime.totalMemory() / 1024;
long dalvikFree = runtime.freeMemory() / 1024;
long dalvikAllocated = dalvikMax - dalvikFree;

所以 dumpsys meminfo <pid> 对应用并非完全无侵入:

(1) 可能触发 ART GC;
(2) 会改变 heap free/alloc;
(3) 可能回收 unreachable Java 对象;
(4) 可能产生短暂暂停;
(5) dump 前后的进程内存状态可能不同。

更微妙的是,执行顺序为:

system_server 先读取 smaps
    ↓
目标进程再执行 Runtime.gc()
    ↓
再读取 Dalvik Heap Alloc/Free 和对象数量


因此同一份输出中:

(1) PSS/RSS 是 GC 前后附近的内核快照;
(2) Dalvik Heap Alloc/Free、对象数量是执行 GC 后的结果。

两者并不是严格同一时刻的数据。

如果要尽量避免对目标进程执行 GC,可以使用:adb shell dumpsys meminfo --local <pid>, 但这样不会得到完整的 Heap Alloc、对象数、SQLite 等应用内部统计。


6.3 对象统计

目标进程还会统计:

(1) View 数量;
(2) ViewRootImpl 数量;
(3) ContextImpl 数量;
(4) Activity 数量;
(5) WebView 数量;
(6) OpenSSL Socket 数量;
(7) Asset/AssetManager 数量;
(8) Binder local/proxy/death object;
(9) Parcel 数量及内存;
(10) SQLite page cache 和数据库信息。

这些都不是 /proc/<pid>/smaps 能提供的。


7. Debug.getMemoryInfo() JNI 实现

//Java 声明:android\frameworks\base\core\java\android\os\Debug.java

public static native void getMemoryInfo(MemoryInfo memoryInfo);
public static native boolean getMemoryInfo(int pid, MemoryInfo memoryInfo);

//JNI 注册位置:android\frameworks\base\core\jni\android_os_Debug.cpp

{ "getMemoryInfo", "(Landroid/os/Debug$MemoryInfo;)V", android_os_Debug_getDirtyPages },
{ "getMemoryInfo", "(ILandroid/os/Debug$MemoryInfo;)Z", android_os_Debug_getDirtyPagesPid },

带 PID 的调用进入:android_os_Debug_getDirtyPagesPid()


8. 详细模式为什么必须读取 smaps,不能只读 smaps_rollup

详细实现中的 load_maps() 位于:android\frameworks\base\core\jni\android_os_Debug.cpp

//明确打开:
std::string smaps_path = StringPrintf("/proc/%d/smaps", pid);
//然后逐个 VMA 调用:
meminfo::ForEachVmaFromFile(smaps_path, vma_scan);

详细模式必须使用 smaps,因为要根据每个 VMA 的名字进行分类:

[heap]
[anon:libc_malloc]
[anon:scudo:*]
[stack:*]
[anon:dalvik-*]
*.so
*.jar
*.apk
*.ttf
*.dex / *.odex
*.vdex
*.oat
*.art
/dev/kgsl-3d0
/dev/ashmem/*
/memfd:jit-cache
...

smaps_rollup 只有进程总量,没有逐 VMA 名称,无法区分 Native Heap、Dalvik Heap、Code、Stack、Ashmem 等类别。

快速统计路径 Debug.getPss() 才使用:/proc/<pid>/smaps_rollup

如果内核不支持再回退到 smaps。实现位于:

//android\system\memory\libmeminfo\procmeminfo.cpp

ProcMemInfo::SmapsOrRollupPss(uint64_t* pss)
ProcMemInfo::SmapsOrRollup(MemUsage* stats)
    std::string path = ::android::base::StringPrintf("/proc/%d/%s", pid_, IsSmapsRollupSupported() ? "smaps_rollup" : "smaps");

因此:

--------------------------------------------------------------
使用场景                        数据源
--------------------------------------------------------------
单 PID 详细 dumpsys meminfo        /proc/<pid>/smaps
多进程快速汇总                    优先 /proc/<pid>/smaps_rollup
Debug.getPss()                    优先 smaps_rollup
需要 VMA 分类                    必须 smaps
--------------------------------------------------------------


9. smaps 字段如何转换成 MemoryInfo

libmeminfo 的解析位于:android\system\memory\libmeminfo\procmeminfo.cpp

主要解析:

Size:
Rss:
Pss:
Private_Clean:
Private_Dirty:
Shared_Clean:
Shared_Dirty:
Swap:
SwapPss:

对应关系如下:

------------------------------------------------------
smaps 字段         MemoryInfo 含义
------------------------------------------------------
Size               虚拟地址空间大小
Rss                当前驻留物理页总量
Pss                共享页按映射者比例分摊后的驻留量
Private_Clean      进程私有、干净、可回收的驻留页
Private_Dirty      进程私有、脏的驻留页
Shared_Clean       共享干净驻留页
Shared_Dirty       共享脏驻留页
Swap               换出页逻辑原始大小
SwapPss            共享换出页按比例分摊后的逻辑大小
------------------------------------------------------

USS 的计算是:uss = private_clean + private_dirty; 也就是当前驻留的私有页面,不包含已换出的私有页。


10. VMA 如何分到 Native/Dalvik/Other

load_maps() 对每个 VMA 分类。

(1) Native Heap

以下映射进入 Native Heap:

[heap]
[anon:libc_malloc]
[anon:scudo:*]
[anon:GWP-ASan*]

 

(2) Dalvik Heap

例如:

[anon:dalvik-alloc space]
[anon:dalvik-main space]
[anon:dalvik-large object space]
[anon:dalvik-non moving space]
[anon:dalvik-zygote space]

(3) Dalvik Other

例如:

dalvik-LinearAlloc
dalvik-indirect ref
dalvik-jit-code-cache
dalvik-CompilerMetadata

(4) Code 和文件映射

按文件扩展名分类:

.so
.jar
.apk
.ttf
.dex/.odex
.vdex
.oat
.art

(5) Device/Ashmem/Graphics

例如:

/dev/kgsl-3d0
/dev/ashmem/CursorWindow
/dev/ashmem/*
/memfd:jit-cache

(6) 未匹配的映射进入:
Unknown
Other mmap
Unknown dev

因此 Unknown 并不一定是泄漏,它只是没有命中 Android 分类规则的剩余映射。


11. SwappablePss 不是 SwapPss

源码中还计算一个容易混淆的字段:swappablePss, 其算法大致是:

sharing_proportion = (pss - uss) / (shared_clean + shared_dirty);

swappable_pss = sharing_proportion * shared_clean + private_clean;

它表示:当前仍驻留、但理论上可以通过丢弃文件页重新加载的 PSS。常见来源:

.so;
.apk;
.jar;
.dex;
.oat;
.art;
字体等 clean file mapping。

它和 ZRAM 无关:

---------------------------------------------------------------------
字段                        含义
---------------------------------------------------------------------
SwappablePss/Pss Clean     当前仍驻留但可丢弃重读的文件页
SwapPss                    已经换出到 swap/ZRAM 的逻辑页
---------------------------------------------------------------------


12. Graphics/memtrack 为什么要额外统计

有些 GPU 内存不会完整出现在 /proc/<pid>/smaps,因此 JNI 还调用 memtrack:

read_memtrack_memory(pid, &graphics_mem);

得到:

graphics;
GL;
other memtrack。

随后人工加入:

stats[HEAP_GRAPHICS].pss = graphics_mem.graphics;
stats[HEAP_GL].pss = graphics_mem.gl;
stats[HEAP_OTHER_MEMTRACK].pss = graphics_mem.other;

同时设置到:

PSS;
Private Dirty;
RSS。

因此可能出现:dumpsys meminfo TOTAL PSS > /proc/<pid>/smaps 中所有 Pss 的总和

差值通常需要检查:

Graphics
GL mtrack
Other mtrack

但 memtrack 依赖 GPU 驱动/HAL 的实现,源码注释也明确提示 graphics 数值可能被驱动错误上报。


13. A12/A14 中 TOTAL PSS 的关键口径

这是需要特别注意的地方。

在两套源码中,Debug.MemoryInfo.getTotalPss() 都定义为:

A12: android\frameworks\base\core\java\android\os\Debug.java
A14: android\frameworks\base\core\java\android\os\Debug.java

public int getTotalPss() {
    return dalvikPss + nativePss + otherPss + getTotalSwappedOutPss();
}

也就是:TOTAL PSS = 驻留 Dalvik PSS + 驻留 Native PSS + 驻留 Other PSS + SwapPss。这意味着在你当前 Android 12/14 产品中:dumpsys meminfo 的分类行 PSS 是驻留 PSS,但最终 TOTAL PSS 已经把 SwapPss 加进去了

例如:

Resident PSS     = 300MB
SwapPss          = 100MB

最终可能显示:

TOTAL PSS:       400MB
TOTAL SWAP PSS:  100MB

此时不能再计算:TOTAL PSS + TOTAL SWAP PSS,否则会把 SwapPss 重复计算一次。这也对统计是否包含压缩进ZRAM的页和pageout的页做一个源码层面的精确补充:

(1) 单独看 Native/Dalvik/Code 等分类行的 PSS:不包含换出页;

(2) 单独看 RSS:不包含换出页;

(3) 看 SwapPss:是换出页的逻辑比例量;

(4) 看最终 TOTAL PSS:在这两套 A12/A14 源码中,已经包含 SwapPss。

 

快速路径 Debug.getPss() 也做了相同处理:

pss += stats.pss;
swapPss = stats.swap_pss;
pss += swapPss;

源码甚至明确注释:// Also in swap, those pages would be accounted as Pss without SWAP

 

14. 输出表中的 Heap Size/Alloc/Free 与 PSS 没有直接等式关系

典型表格:

# dumpsys meminfo 3222
                   Pss  Private  Private  SwapPss   Rss   Heap   Heap   Heap
                 Total    Dirty    Clean    Dirty  Total   Size  Alloc   Free
 Native Heap
 Dalvik Heap
 ...
 TOTAL

其中左半部分来自内核和 memtrack:

Pss

Private Dirty

Private Clean

SwapPss

Rss

右半部分来自目标进程内部:

Heap Size

Heap Alloc

Heap Free

所以不能假设:Native Heap Alloc == Native Heap PSS, 可能出现:

Native Heap Alloc   500MB

Native Heap PSS    100MB

Native SwapPss      300MB

原因包括:分配但尚未 fault 的虚拟页;页面进入 ZRAM;allocator arena 已保留但部分页可回收;内存碎片;madvise;共享/COW;采集时间不一致。

 

15. App Summary 是重新归类,不是表格简单相加

打印格式如:

# dumpsys meminfo 3222
 App Summary
                       Pss(KB)                        Rss(KB)
                        ------                         ------
           Java Heap:    57356                          97188
         Native Heap:   543024                         551016
                Code:   545428                         734936
               Stack:    10060                          10072
            Graphics:   620190                         620190
       Private Other:   718068
              System:    19149
             Unknown:                                  728924

           TOTAL PSS:  2513275            TOTAL RSS:  2742326      TOTAL SWAP (KB):        0

App Summary 的核心计算位于:android\frameworks\base\core\java\android\os\Debug.java

 

大致口径如下。

(1) Java Heap

dalvikPrivateDirty + private ART image

(2) Native Heap

nativePrivateDirty

(3) Code

包括私有部分:

.so
.jar
.apk
.ttf
.dex
.oat
JIT code cache

(4) Stack

Stack private dirty

(5) Graphics

GL device private + Graphics memtrack + GL memtrack

(6) Private Other

全部 Private Clean/Dirty - Java Heap - Native Heap - Code - Stack - Graphics

(7) System

getTotalPss() - getTotalPrivateClean() - getTotalPrivateDirty()

因为这里的 getTotalPss() 已包含 SwapPss,所以 swap 部分可能落在 Summary 的残差/System 口径中。不要把 App Summary 各项理解成严格的“物理 RAM 分类”。

 

16. Native 进程的处理不同

如果 PID 没有对应 Android ProcessRecord,AMS 会尝试在 ProcessCpuTracker 中查找 native process。

路径位于:android\frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java

然后直接在 system_server 中:

Debug.getMemoryInfo(pid, mi);
ActivityThread.dumpMemInfoTable(...);

不会调用目标进程的 IApplicationThread,因为 native daemon 没有该 Binder 接口。所以对 native 进程:

(1) PSS/RSS/SwapPss/VMA 分类仍可获得;

(2) memtrack 可能可获得;

(3) Native Heap Size/Alloc/Free 通常显示 0 或缺失;

(4) 没有 Java Heap;

(5) 没有 View/Activity/SQLite/Binder object 等运行时信息。

 

17. 性能与准确性限制

17.1 详细模式比较重

读取 /proc/<pid>/smaps 需要:遍历所有 VMA;扫描页表;计算每页 mapcount/PSS;解析每个 VMA 的统计。进程 VMA 很多时,执行时间会明显增加。smaps_rollup 更快,但无法做 Native/Dalvik/Code 等分类。

17.2 不是原子快照

采集过程中目标进程仍可能:malloc/free;mmap/munmap;fault/page-out;创建/退出线程;修改共享页。因此不同统计项之间可能有小幅不一致

17.3 dump 本身会改变目标状态

普通应用详细 dump 会:发送 Binder;临时解冻进程;执行 GC;遍历对象;查询 SQLite;访问 allocator 状态。如果用于性能或低内存问题分析,不建议高频执行详细 dumpsys meminfo。

17.4 ZRAM 压缩后实际大小不可按 PID获得

SwapPss 是换出页的逻辑原始大小,不是压缩后大小。实际 ZRAM 全局物理成本仍需查看:cat /sys/block/zram0/mm_stat, 重点字段:

orig_data_size
compr_data_size
mem_used_total

其中 mem_used_total 最接近 ZRAM 实际物理成本,但不能精确归属到单个 PID。

 

18. 推荐验证方式

对同一个 PID 同时抓取:

PID=<pid>
dumpsys meminfo $PID
dumpsys meminfo --local $PID
cat /proc/$PID/smaps_rollup
grep -E '^(Rss|Pss|Private|Shared|Swap|SwapPss):' /proc/$PID/smaps_rollup

cat /sys/block/zram0/mm_stat

预期关系大致是:

dumpsys 分类 PSS 合计 ≈ smaps resident PSS + memtrack

dumpsys TOTAL RSS ≈ smaps RSS + memtrack RSS

dumpsys TOTAL SWAP PSS ≈ smaps SwapPss

dumpsys TOTAL PSS ≈ smaps PSS + smaps SwapPss + memtrack

最后可以把实现原理概括为:dumpsys meminfo <pid> 由 AMS 的独立 meminfo Binder 服务驱动:system_server 逐 VMA 读取目标进程的 smaps,按映射名称分类并叠加 memtrack;对 Android 应用再通过 IApplicationThread 调用目标进程,获取 GC 后的 ART/malloc heap、对象、Binder、SQLite 等运行时信息,最后由 ActivityThread.dumpMemInfoTable()统一格式化。当前 A12/A14 源码的最终 TOTAL PSS 已包含 SwapPss,而 TOTAL RSS 只表示当前驻留页

 

二、trace实测验证

A14_user_2026-09-15_14-32-03.trace //dumpsys meminfo, pid=30345

A14_user_2026-09-15_14-52-52.trace //dumpsys meminfo 3222, pid=8139

A14_user_2026-09-15_14-53-46.trace //dumpsys meminfo --local 3222, pid=8508

1. 逻辑和负载进程分布

(1) dumpsys 进程的主线程向 binder 域的 SMGR 获取 meminfo 服务,然后 fork 一个辅线程,辅线程向 system_server 发同比 binder 传输 (code = DUMP_TRANSACTION = 0x5f444d50),
(2) 整个 dumpsys 负载主要在 system_server 的响应 binder 线程上。它会访问 memtrack 服务,调用 getMemory_GL_Mali()、getMemory_GRAPHICS_ion()、getMemory_GRAPHICS_dmaheap() 等,每个进程都会调用一一组,若使用 dumpsys meminfo <pid> 就只会调用一组了。
 
2. 是否加 "--local" trace上的现象没有任何变化。


三、dumpsys meminfo 与 dumpsys meminfo <pid> 区别

1. 二者对比

----------------------------------------------------------------------------------------------------
对比项                         dumpsys meminfo <pid>               dumpsys meminfo
----------------------------------------------------------------------------------------------------
统计范围                        指定一个进程                         全部应用进程和 native 进程
dumpDetails                    单应用 PID 自动为 true               默认 false
读取 smaps                     是,读取一个进程                      是,默认读取所有进程
读取 memtrack                  是                                  是,对多个进程
Binder 回调目标应用              是(实测A14是,异步binder发起的)        默认否
目标应用执行 GC                  是                                 默认否
Native/Dalvik Heap Alloc       有                                 默认不逐进程输出
Objects/View/Activity          有                                 默认无
SQLite/DB 信息                  有                                默认无
按进程排序                       主要看单进程                        有
按 OOM adj 汇总                 意义有限                            有,主要用途之一
按内存类别汇总                   单进程分类                           全系统分类
Total/Free/Used/Lost RAM       可能经过公共输出路径,但不是重点        核心输出
ZRAM/KSM/DMA-BUF/GPU           公共 footer 可能显示                核心系统级数据
对目标进程侵入性                  较高,会 GC                        不逐进程 GC,但全局扫描较重
典型用途                         定位某应用的 heap/映射/泄漏          判断整机 RAM 分布和最大占用者
----------------------------------------------------------------------------------------------------


2. native PID 的特殊情况

dumpsys meminfo $(pidof surfaceflinger)

如果 PID 没有对应 Android ProcessRecord,AMS 会进入 native process 分支:

ProcessCpuTracker 查找 PID --> system_server 调用 Debug.getMemoryInfo(pid) --> 本地 dumpMemInfoTable。

因为 native daemon 没有 IApplicationThread:不会 Binder 回调目标进程;不会触发目标进程 GC;能看到 PSS/RSS/SwapPss/VMA 分类;Native Heap Size/Alloc/Free 通常为 0 或不可用;没有 Activity/View/SQLite 等信息。

所以“带 PID 一定会触发 GC”只对 Android Java 应用进程成立,不适用于普通 native daemon。


3. 参数组合会改变行为

3.1 单 PID 加 --local

dumpsys meminfo --local <pid>

仍然由 system_server 读取:/proc/<pid>/smaps,但不调用:IApplicationThread.dumpMemInfo(),优点:不触发目标进程 GC####(实测的确没有向目标进程发异步binder出发GC);对被测应用干扰较小。缺点:
Heap Size/Alloc/Free 不完整;没有对象、SQLite、Asset 等运行时信息。适合做低干扰的单进程 PSS/RSS/SwapPss 观察。


3.2 不带 PID加 -a

dumpsys meminfo -a

-a 会显式设置:

dumpDetails = true;
dumpFullDetails = true;
dumpDalvik = true;

这可能对每个 Android 应用调用 dumpMemInfo(),从而逐个触发 GC。这是非常重的命令,不建议在:性能测试;卡顿分析;内存压力测试;启动耗时测试;期间高频执行。

实测,相当于对每一个 pid 都执行一次 dumpsys meminfo <pid> 


3.3 不带 PID加 --oom

dumpsys meminfo --oom

oomOnly=true 后,不需要完整分类时可以进入:Debug.getPss(pid, ...), 快速路径优先读取:/proc/<pid>/smaps_rollup, 它通常比遍历每个进程的完整 smaps 更轻,适合观察各 oom_adj 层级的总体内存。

 

posted on 2026-09-15 15:37  Hello-World3  阅读(12)  评论(0)    收藏  举报

导航