源码基于:Android U
参考:
dumpsys meminfo 详解(R)
dumpsys meminfo 详解(U)
1. 命令入口 MemBinder
frameworks/base/services/core/java/com/android/server/am/AMS.java
static class MemBinder extends Binder {
ActivityManagerService mActivityManagerService;
MemBinder(ActivityManagerService activityManagerService) {
mActivityManagerService = activityManagerService;
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
...
}
}
2. 根据参数收集应用层 procs
dump 流程首先会调用 collectProcesses() 函数来解析需要dump 的进程 ProcessRecord,并且会将该 ProcessRecord list 以参数形式传入 dumpApplicationMemoryUsage() 函数,这也是 dump 的核心处理函数。
frameworks/base/services/core/java/com/android/server/am/AMS.java
ArrayList<ProcessRecord> collectProcesses(PrintWriter pw, int start, boolean allPkgs,
String[] args) {
synchronized (mProcLock) {
return mProcessList.collectProcessesLOSP(start, allPkgs, args);
}
}
详细的代码可以查看 dumpsys meminfo 详解(U) 一文。
代码还是比较清晰的:
- 如果命令行指定了pid,那么就收集这些进程;
- 如果设定了packageName,就收集这些package;
- 如果没有设定,则收集所有的 LRU process;
注意,前两点有可能收集的 proc 为空,因为设定的参数有可能是假的或者无法匹配。
这个时候终端上还提示No process:
shift:/ # dumpsys meminfo 12345
No process found for: 12345
3. 根据参数收集native层procs
在 dumpApplicationMemoryUsage() 函数中会看到这样一部分代码:
frameworks/base/services/core/java/com//android/server/am/AMS.java
if (collectNative) {
mi = null;
final Debug.MemoryInfo[] memInfos = new Debug.MemoryInfo[1];
mAppProfiler.forAllCpuStats((st) -> {
if (st.vsize > 0 && procMemsMap.indexOfKey(st.pid) < 0) {
...
}
});
ArrayList<MemItem> catMems = new ArrayList<MemItem>();
catMems.add(new MemItem("Native", "Native",
ss[INDEX_NATIVE_PSS], ss[INDEX_NATIVE_SWAP_PSS], ss[INDEX_NATIVE_RSS], -1));
...
}
这部分代码是继处理应用层 LRU procs 之后,接着dump native 进程 procs 的内存。
其中入口需要 collectNative 为true,来看下初始化的地方:
final boolean collectNative = !opts.isCheckinRequest && numProcs > 1 && !opts.packages;
只要命令行没有配置 --checkin 参数,且没有配置 --package 参数,且LRU 的proc 大于1,则该值为 true。
其实,当dump 多个应用或者所有应用的 meminfo 时,变量 collectNative 被置true;如果只是打印单个应用的 meminfo 时该值为 false,所以最终不会打印。即,collectNative 也是用来区分 dump 所有应用meminfo 还是dump 单个应用的 meminfo。
当 collectNative 为true 时,另外一个很重要的事情就是通过 MemInfoReader.readMemInfo() 获取系统/proc/meminfo 的内存信息。
4. oomOnly 影响内存收集
当收集好 procs 之后,会通过 for 循环进行轮询,通过Debug 提供的内存获取函数,分别统计每个 pid 对应的 smaps 信息。
但,收集内存的函数有两个选择:
- Debug.getMemoryInfo()
- Debug.getPss()
if (!brief && !opts.oomOnly) {
...
if (!Debug.getMemoryInfo(st.pid, info)) {
return;
}
} else {
long pss = Debug.getPss(st.pid, tmpLong, memtrackTmp);
...
}
brief 是从 MemBinder 传入,默认为 false;
opts.oomOnly 是命令行是否带有 --oom 参数的flag,如果不带则调用 getMemoryInfo() 函数,如果带有则调用 getPss() 函数。区别在于 oomOnly 如果为 true时,不需要dump category 信息,而当 oomOnly 为false 时,需要 dump category 信息:
Total PSS by category:
330,928K: EGL mtrack
270,815K: Native
240,812K: Dalvik
168,739K: .apk mmap
154,683K: .art mmap
140,548K: .so mmap
109,070K: .dex mmap
99,964K: GL mtrack
92,736K: Dalvik Other
44,257K: .jar mmap
36,406K: Other mmap
33,019K: Stack
31,307K: Unknown
15,729K: .oat mmap
1,670K: Other dev
1,013K: Ashmem
848K: .ttf mmap
0K: Cursor
0K: Gfx dev
0K: Other mtrack
所以,getMemoryInfo() 中会统计更详细的每个 which_heap 的 pss、rss、swap 等等信息,而 getPss() 函数则只需要统计每个进程的 rss、pss、swap 整体信息即可。
4.1 Debug.getMemoryInfo()
frameworks/base/core/jni/android_os_Debug.cpp
static jboolean android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
jint pid, jobject object)
{
bool foundSwapPss;
stats_t stats[_NUM_HEAP];
memset(&stats, 0, sizeof(stats));
if (!load_maps(pid, stats, &foundSwapPss)) {
return JNI_FALSE;
}
struct graphics_memory_pss graphics_mem;
if (read_memtrack_memory(pid, &graphics_mem) == 0) {
stats[HEAP_GRAPHICS].pss = graphics_mem.graphics;
stats[HEAP_GRAPHICS].privateDirty = graphics_mem.graphics;
stats[HEAP_GRAPHICS].rss = graphics_mem.graphics;
stats[HEAP_GL].pss = graphics_mem.gl;
stats[HEAP_GL].privateDirty = graphics_mem.gl;
stats[HEAP_GL].rss = graphics_mem.gl;
stats[HEAP_OTHER_MEMTRACK].pss = graphics_mem.other;
stats[HEAP_OTHER_MEMTRACK].privateDirty = graphics_mem.other;
stats[HEAP_OTHER_MEMTRACK].rss = graphics_mem.other;
}
for (int i=_NUM_CORE_HEAP; i<_NUM_EXCLUSIVE_HEAP; i++) {
stats[HEAP_UNKNOWN].pss += stats[i].pss;
stats[HEAP_UNKNOWN].swappablePss += stats[i].swappablePss;
stats[HEAP_UNKNOWN].rss += stats[i].rss;
stats[HEAP_UNKNOWN].privateDirty += stats[i].privateDirty;
stats[HEAP_UNKNOWN].sharedDirty += stats[i].sharedDirty;
stats[HEAP_UNKNOWN].privateClean += stats[i].privateClean;
stats[HEAP_UNKNOWN].sharedClean += stats[i].sharedClean;
stats[HEAP_UNKNOWN].swappedOut += stats[i].swappedOut;
stats[HEAP_UNKNOWN].swappedOutPss += stats[i].swappedOutPss;
}
for (int i=0; i<_NUM_CORE_HEAP; i++) {
env->SetIntField(object, stat_fields[i].pss_field, stats[i].pss);
env->SetIntField(object, stat_fields[i].pssSwappable_field, stats[i].swappablePss);
env->SetIntField(object, stat_fields[i].rss_field, stats[i].rss);
env->SetIntField(object, stat_fields[i].privateDirty_field, stats[i].privateDirty);
env->SetIntField(object, stat_fields[i].sharedDirty_field, stats[i].sharedDirty);
env->SetIntField(object, stat_fields[i].privateClean_field, stats[i].privateClean);
env->SetIntField(object, stat_fields[i].sharedClean_field, stats[i].sharedClean);
env->SetIntField(object, stat_fields[i].swappedOut_field, stats[i].swappedOut);
env->SetIntField(object, stat_fields[i].swappedOutPss_field, stats[i].swappedOutPss);
}
env->SetBooleanField(object, hasSwappedOutPss_field, foundSwapPss);
jintArray otherIntArray = (jintArray)env->GetObjectField(object, otherStats_field);
jint* otherArray = (jint*)env->GetPrimitiveArrayCritical(otherIntArray, 0);
if (otherArray == NULL) {
return JNI_FALSE;
}
int j=0;
for (int i=_NUM_CORE_HEAP; i<_NUM_HEAP; i++) {
otherArray[j++] = stats[i].pss;
otherArray[j++] = stats[i].swappablePss;
otherArray[j++] = stats[i].rss;
otherArray[j++] = stats[i].privateDirty;
otherArray[j++] = stats[i].sharedDirty;
otherArray[j++] = stats[i].privateClean;
otherArray[j++] = stats[i].sharedClean;
otherArray[j++] = stats[i].swappedOut;
otherArray[j++] = stats[i].swappedOutPss;
}
env->ReleasePrimitiveArrayCritical(otherIntArray, otherArray, 0);
return JNI_TRUE;
}
- 通过 load_maps() 从 /proc/pid/smaps 中获取进程的内存信息;
- 通过 read_memtrack_memory() 通过 libmemtrack.so 获取 graphics 的内存信息;
- 通过 env->SetIntField() 将 UNKNOWN、dalvik、heap 所属组的信息设置到 java 接口的参数 Meminfo 中;
- 通过 otherArray 数组对应 java 端 MemoryInfo.otherStats[] 数组;
注意:
除了 HEAP_DALVIK 和 HEAP_NATIVE,其他所有 which_heap 的信息都会累加到 HEAP_UNKNOWN 中。这样,在 AMS 中会统计重要的字段信息,其他都认为是 unknown,如下:
Total PSS by category:
330,928K: EGL mtrack
270,815K: Native
240,812K: Dalvik
168,739K: .apk mmap
154,683K: .art mmap
140,548K: .so mmap
109,070K: .dex mmap
99,964K: GL mtrack
92,736K: Dalvik Other
44,257K: .jar mmap
36,406K: Other mmap
33,019K: Stack
31,307K: Unknown
15,729K: .oat mmap
1,670K: Other dev
1,013K: Ashmem
848K: .ttf mmap
0K: Cursor
0K: Gfx dev
0K: Other mtrack
4.2 Debug.getPss()
frameworks/base/core/jni/android_os_Debug.cpp
static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid,
jlongArray outUssSwapPssRss, jlongArray outMemtrack)
{
jlong pss = 0;
jlong rss = 0;
jlong swapPss = 0;
jlong uss = 0;
jlong memtrack = 0;
struct graphics_memory_pss graphics_mem;
if (read_memtrack_memory(pid, &graphics_mem) == 0) {
pss = uss = rss = memtrack = graphics_mem.graphics + graphics_mem.gl + graphics_mem.other;
}
::android::meminfo::ProcMemInfo proc_mem(pid);
::android::meminfo::MemUsage stats;
if (proc_mem.SmapsOrRollup(&stats)) {
pss += stats.pss;
uss += stats.uss;
rss += stats.rss;
swapPss = stats.swap_pss;
pss += swapPss; // Also in swap, those pages would be accounted as Pss without SWAP
} else {
return 0;
}
if (outUssSwapPssRss != NULL) {
int outLen = env->GetArrayLength(outUssSwapPssRss);
if (outLen >= 1) {
jlong* outUssSwapPssRssArray = env->GetLongArrayElements(outUssSwapPssRss, 0);
if (outUssSwapPssRssArray != NULL) {
outUssSwapPssRssArray[0] = uss;
if (outLen >= 2) {
outUssSwapPssRssArray[1] = swapPss;
}
if (outLen >= 3) {
outUssSwapPssRssArray[2] = rss;
}
}
env->ReleaseLongArrayElements(outUssSwapPssRss, outUssSwapPssRssArray, 0);
}
}
if (outMemtrack != NULL) {
int outLen = env->GetArrayLength(outMemtrack);
if (outLen >= 1) {
jlong* outMemtrackArray = env->GetLongArrayElements(outMemtrack, 0);
if (outMemtrackArray != NULL) {
outMemtrackArray[0] = memtrack;
if (outLen >= 2) {
outMemtrackArray[1] = graphics_mem.graphics;
}
if (outLen >= 3) {
outMemtrackArray[2] = graphics_mem.gl;
}
if (outLen >= 4) {
outMemtrackArray[3] = graphics_mem.other;
}
}
env->ReleaseLongArrayElements(outMemtrack, outMemtrackArray, 0);
}
}
return pss;
}
- 通过 read_memtrace_memory() 统计graphics 的内存信息,并都累计到总的 pss、rss、uss、memtrack 中;
- 通过 proc_mem.SmapsOrRollup() 函数统计 /proc/pid/smaps 中 Pss、Private_Clean、Private_Dirty、Rss、SwapPss 等信息;
- 该函数返回的是进程的 pss,包括 swapPss。其他数据通过两个数组参数回传到 java端:
- tmpLong 的三个元素分别是:uss、swap pss、rss;
- memtrackTmp 的四个元素分别是:memtrack 总和、graphics、gl、other;
注意:
无论 getMemoryInfo() 之后在 AMS 中计算,还是getPss() 在 android_os_Debug.cpp 中计算,total 的pss,都是需要加上 swap out pss;
5. graphics 的内存收集函数
如上,无论是 getMemoryInfo() 还是 getPss() 函数,都会调用 read_memtrace_memory() 函数收集 graphics 的内存。
主要是通过libmemtrack.so,详细可以查看 dumpsys meminfo 详解(U) 一文。
6. dumpDetails 影响细节打印
opts.dumpDetails 是命令行是否带有 -a 或 -s 参数的 flag,当带有该参数时需要收集进程的所有category 信息。所以,收集函数是调用 getMemoryInfo(),而不是 getPss()。
另外,当该 flag 为true时,dump 流程会调用 thread.dumpMemInfo() 函数,会通过 getRuntime() 获取app 进程 dalvik 的 totalMemory 和 freeMemory,并计算出 dalvikAllocated,得到app 进程虚拟机内存使用情况。 详细可以查看 dumpsys meminfo 详解(U) 一文。
最终结果如下图:
7. which_heap 的概念
whick_heap 的概念在 android_os_Debug.cpp 中提出,用以区分某 pid 应用的内存。在 native 端有个 enum:
frameworks/base/core/jni/android_os_Debug.cpp
enum {
HEAP_UNKNOWN,
HEAP_DALVIK,
HEAP_NATIVE,
HEAP_DALVIK_OTHER,
HEAP_STACK,
HEAP_CURSOR,
HEAP_ASHMEM,
HEAP_GL_DEV,
HEAP_UNKNOWN_DEV,
HEAP_SO,
HEAP_JAR,
HEAP_APK,
HEAP_TTF,
HEAP_DEX,
HEAP_OAT,
HEAP_ART,
HEAP_UNKNOWN_MAP,
HEAP_GRAPHICS,
HEAP_GL,
HEAP_OTHER_MEMTRACK,
...
_NUM_HEAP,
_NUM_EXCLUSIVE_HEAP = HEAP_OTHER_MEMTRACK+1,
_NUM_CORE_HEAP = HEAP_NATIVE+1
};
当然,该分类对应 Debug.MemoryInfo中有相同值的静态变量,如下:
frameworks/base/core/java/android/os/Debug.java
public static final int HEAP_UNKNOWN = 0;
/** @hide */
public static final int HEAP_DALVIK = 1;
/** @hide */
public static final int HEAP_NATIVE = 2;
/** @hide */
public static final int OTHER_DALVIK_OTHER = 0;
/** @hide */
public static final int OTHER_STACK = 1;
/** @hide */
public static final int OTHER_CURSOR = 2;
/** @hide */
...
注意:
系统将 smaps 中的内存分成了三大块:dalvik、native 以及 unknown,并且系统将 unknown 又细分了17 类。
7.1 dumpsys meminfo 中的category
Total PSS by category:
260,780K: Native
255,280K: EGL mtrack
213,776K: Dalvik
186,632K: .apk mmap
131,867K: .so mmap
110,157K: .dex mmap
109,952K: GL mtrack
87,703K: Dalvik Other
79,525K: .art mmap
47,593K: .jar mmap
41,380K: Other mmap
31,408K: Stack
29,258K: Unknown
17,319K: .oat mmap
1,544K: Other dev
887K: Ashmem
510K: .ttf mmap
0K: Cursor
0K: Gfx dev
0K: Other mtrack
其中,除了 Native、Dalvik 和 Unknown,其他的是 unknown 细分出来的 17 类。
而,上面的 Unknown 是从大块 Unknown 中去除其他 17 类之后的内存。
7.2 smaps 中vma 与 HEAP 枚举对照
对照表可以查看 dumpsys meminfo 详解(U) 一文。
8. total pss
totap pss 分两大块:应用进程的 total pss + native 进程的 total pss。每个进程的 total pss 有如下的公式:
total pss = dalvikPss + nativePss + otherPss + total swap out pss;
即,系统的 total pss 为所有应用进程的total pss 与所有native 进程的 total pss 之和。
其中每个进程的 total swap out pss 满足:
frameworks/base/core/java/android/os/Debug.java
public int getTotalSwappedOutPss() {
return dalvikSwappedOutPss + nativeSwappedOutPss + otherSwappedOutPss;
}
9. cached pss
cached pss 为应用进程中 oomAdj 大于等于 ProcessList.CACHED_APP_MIN_ADJ 的所有进程 total pss 之和。
10. 系统 memory
详细可以查看 dumpsys meminfo 详解(U) 一文。