[toc]
我们使用 adb shell dumpsys meminfo <包名> 的时候,会输出应用的内存信息。
该功能是以一种系统服务的形式,被注册到 ServiceManager 中,等待 binder 的调用。
一、注册服务
在系统启动的时候,在 Systemerver 中会开启一系列的系统服务,
1.1 -> frameworks/base/services/java/com/android/server/SystemServer.java
public static void main(String[] args) {
new SystemServer().run();
}
private void run() {
...
try {
t.traceBegin("StartServices");
// 启动 Bootstrap 服务
startBootstrapServices(t);
startCoreServices(t);
startOtherServices(t);
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
} finally {
t.traceEnd(); // StartServices
}
...
}
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
...
mActivityManagerService = ActivityManagerService.Lifecycle.startService(
mSystemServiceManager, atm);
...
// Set up the Application instance for the system process and get started.
t.traceBegin("SetSystemProcess");
// 主要注册获取进程各种信息的服务
mActivityManagerService.setSystemProcess();
t.traceEnd();
}
1.2 ->frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
// 添加各种服务,如 meminfo、gfxinfo、dbinfo、cpuinfo、permission、processinfo、cacheinfo
public void setSystemProcess() {
try {
...
// 添加 MemBinder ,服务名称为 meminfo
ServiceManager.addService("meminfo", new MemBinder(this), /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_HIGH);
...
}
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(
"Unable to find android system package", e);
}
...
}
二、Binder 实现
通过上文,我们知道了 dumpsys meminfo 的实现是通过 一个 Binder 在 ServiceManager 中
具体的实现在 MemBinder 中,该类为 AMS 的内部类
2.1 ->frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
static class MemBinder extends Binder {
ActivityManagerService mActivityManagerService;
private final PriorityDump.PriorityDumper mPriorityDumper =
new PriorityDump.PriorityDumper() {
@Override
public void dumpHigh(FileDescriptor fd, PrintWriter pw, String[] args,
boolean asProto) {
dump(fd, pw, new String[] {"-a"}, asProto);
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
// 最终实现
mActivityManagerService.dumpApplicationMemoryUsage(
fd, pw, " ", args, false, null, asProto);
}
};
MemBinder(ActivityManagerService activityManagerService) {
mActivityManagerService = activityManagerService;
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
try {
if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
Process.enableFreezer(false);
}
if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
"meminfo", pw)) return;
PriorityDump.dump(mPriorityDumper, fd, pw, args);
} finally {
if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
Process.enableFreezer(true);
}
}
}
}
2.2 ->frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
我们再来看 AMS 的方法
final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, String prefix,
String[] args, boolean brief, PrintWriter categoryPw, boolean asProto) {
MemoryUsageDumpOptions opts = new MemoryUsageDumpOptions();
// 省略一系列的参数解析
...
String[] innerArgs = new String[args.length-opti];
System.arraycopy(args, opti, innerArgs, 0, args.length-opti);
// 这里处理了后面跟的 包名/pid ,限定输出特定进程信息,如果 opts.packages 为 true,则输出所有进程
ArrayList<ProcessRecord> procs = collectProcesses(pw, opti, opts.packages, args);
if (opts.dumpProto) {
dumpApplicationMemoryUsage(fd, opts, innerArgs, brief, procs);
} else {
// 这里看未添加 --proto 参数的命令
dumpApplicationMemoryUsage(fd, pw, prefix, opts, innerArgs, brief, procs, categoryPw);
}
}
2.3 ->frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
ASM 中获取内存信息的方法,包括了读取 smaps
private final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, String prefix,
MemoryUsageDumpOptions opts, String[] innerArgs, boolean brief,
ArrayList<ProcessRecord> procs, PrintWriter categoryPw) {
// 记录时间,输出时使用
// eg -> Uptime: 2462590 Realtime: 2462590
long uptime = SystemClock.uptimeMillis();
long realtime = SystemClock.elapsedRealtime();
final long[] tmpLong = new long[3];
if (procs == null) { // 一般我们会传入进程包名,暂不分析此处逻辑
// 传入的进程集合为空,判断是否输出 native 进程,否则输出 -> No process found for:XXXX
...
}
// 是否要输出详细信息,比如我们常用的传入 包名/PID 则为 true
if (!brief && !opts.oomOnly && (procs.size() == 1 || opts.isCheckinRequest || opts.packages)) {
// 设置 dumpDetails 为 ture
opts.dumpDetails = true;
}
final int numProcs = procs.size();
// 不带 --checkin ,并且 numProcs > 1, 并且不带 --package 才为 true,一般我们使用传入一个进程名称 numProcs 为 1
final boolean collectNative = !opts.isCheckinRequest && numProcs > 1 && !opts.packages;
if (collectNative) {
// If we are showing aggregations, also look for native processes to
// include so that our aggregations are more accurate.
updateCpuStatsNow();
}
// 打印 Header
// Applications Memory Usage (in Kilobytes): ...
dumpApplicationMemoryUsageHeader(pw, uptime, realtime, opts.isCheckinRequest, opts.isCompact);
// 初始化各种变量
...
// 关键变量 mi ,MemoryInfo 类型
Debug.MemoryInfo mi = null;
// 只传入一个进程包名,此处 numProcs 为 1
for (int i = numProcs - 1; i >= 0; i--) {
final ProcessRecord r = procs.get(i);
final IApplicationThread thread;
final int pid;
final int oomAdj;
final boolean hasActivities;
synchronized (this) {
// 获取 IApplicationThread 远端代理,用于和包名进程通信,最终的打印在目标进程,
// 统计 Java 相关的一些数据在其进程中通过 Runtime 进行获取,比如 dalvikMax、dalvikFree、dalvikAllocated
thread = r.thread;
pid = r.pid;
oomAdj = r.getSetAdjWithServices();
hasActivities = r.hasActivities();
}
if (thread != null) {
if (mi == null) {
// 构造 mi 对象
mi = new Debug.MemoryInfo();
}
final int reportType;
final long startTime;
final long endTime;
// 仅传入包名的命令此处为 true
if (opts.dumpDetails || (!brief && !opts.oomOnly)) {
reportType = ProcessStats.ADD_PSS_EXTERNAL_SLOW;
startTime = SystemClock.currentThreadTimeMillis();
// 关键方法,此处根据 pid 对 mi 对象内部成员进行赋值,转到 3-1 Debug.getMemoryInfo(pid,MemoryInfo)
// 这里很有必要关注一下 Memofynfo 这个类的成员变量,后面的输出基本与该类有关。
// 转到 附1 -> frameworks/base/core/java/android/os/Debug.java#MemoryInfo
if (!Debug.getMemoryInfo(pid, mi)) {
continue;
}
endTime = SystemClock.currentThreadTimeMillis();
hasSwapPss = mi.hasSwappedOutPss;
} else {
reportType = ProcessStats.ADD_PSS_EXTERNAL;
startTime = SystemClock.currentThreadTimeMillis();
long pss = Debug.getPss(pid, tmpLong, null);
if (pss == 0) {
continue;
}
mi.dalvikPss = (int) pss;
endTime = SystemClock.currentThreadTimeMillis();
mi.dalvikPrivateDirty = (int) tmpLong[0];
mi.dalvikRss = (int) tmpLong[2];
}
// 根据上文份分析,仅传入包名 isCheckinRequest:false ,dumpDetails:true
if (!opts.isCheckinRequest && opts.dumpDetails) {
// 输出 ** MEMINFO in pid 6555 [<应用包名 xxx>] **
pw.println("\n** MEMINFO in pid " + pid + " [" + r.processName + "] **");
}
if (opts.dumpDetails) {
// 对应 --local,此处传入仅包名需要调起进程进行输出,此处为 false
if (opts.localOnly) {
ActivityThread.dumpMemInfoTable(pw, mi, opts.isCheckinRequest, opts.dumpFullDetails,
opts.dumpDalvik, opts.dumpSummaryOnly, pid, r.processName, 0, 0, 0, 0, 0, 0);
if (opts.isCheckinRequest) {
pw.println();
}
} else {
// Flushes stream.
pw.flush();
try {
TransferPipe tp = new TransferPipe();
try {
// 通过 IApplicatonThread 进行 Binder 调用,进入目标进程处理流程
// 转到 4.1-> frameworks/base/core/java/android/app/ActivityThread.java#ApplicationThread#dumpMemInfo
thread.dumpMemInfo(tp.getWriteFd(),
mi, opts.isCheckinRequest, opts.dumpFullDetails,
opts.dumpDalvik, opts.dumpSummaryOnly, opts.dumpUnreachable, innerArgs);
tp.go(fd, opts.dumpUnreachable ? 30000 : 5000);
} finally {
tp.kill();
}
} catch (IOException e) {
if (!opts.isCheckinRequest) {
pw.println("Got IoException! " + e);
pw.flush();
}
} catch (RemoteException e) {
if (!opts.isCheckinRequest) {
pw.println("Got RemoteException! " + e);
pw.flush();
}
}
}
}
final long myTotalPss = mi.getTotalPss();
final long myTotalUss = mi.getTotalUss();
final long myTotalRss = mi.getTotalRss();
final long myTotalSwapPss = mi.getTotalSwappedOutPss();
synchronized (this) {
if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
// Record this for posterity if the process has been stable.
r.baseProcessTracker.addPss(myTotalPss, myTotalUss, myTotalRss, true,
reportType, endTime-startTime, r.pkgList.mPkgList);
for (int ipkg = r.pkgList.size() - 1; ipkg >= 0; ipkg--) {
ProcessStats.ProcessStateHolder holder = r.pkgList.valueAt(ipkg);
FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
r.info.uid,
holder.state.getName(),
holder.state.getPackage(),
myTotalPss, myTotalUss, myTotalRss, reportType,
endTime-startTime,
holder.appVersion);
}
}
}
if (!opts.isCheckinRequest && mi != null) {
totalPss += myTotalPss;
totalSwapPss += myTotalSwapPss;
totalRss += myTotalRss;
MemItem pssItem = new MemItem(r.processName + " (pid " + pid +
(hasActivities ? " / activities)" : ")"), r.processName, myTotalPss,
myTotalSwapPss, myTotalRss, pid, hasActivities);
procMems.add(pssItem);
procMemsMap.put(pid, pssItem);
nativePss += mi.nativePss;
nativeSwapPss += mi.nativeSwappedOutPss;
nativeRss += mi.nativeRss;
dalvikPss += mi.dalvikPss;
dalvikSwapPss += mi.dalvikSwappedOutPss;
dalvikRss += mi.dalvikRss;
for (int j=0; j<dalvikSubitemPss.length; j++) {
dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
dalvikSubitemSwapPss[j] +=
mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
dalvikSubitemRss[j] += mi.getOtherRss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
}
otherPss += mi.otherPss;
otherRss += mi.otherRss;
otherSwapPss += mi.otherSwappedOutPss;
for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
long mem = mi.getOtherPss(j);
miscPss[j] += mem;
otherPss -= mem;
mem = mi.getOtherSwappedOutPss(j);
miscSwapPss[j] += mem;
otherSwapPss -= mem;
mem = mi.getOtherRss(j);
miscRss[j] += mem;
otherRss -= mem;
}
if (oomAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
cachedPss += myTotalPss;
cachedSwapPss += myTotalSwapPss;
}
for (int oomIndex=0; oomIndex<oomPss.length; oomIndex++) {
if (oomIndex == (oomPss.length - 1)
|| (oomAdj >= DUMP_MEM_OOM_ADJ[oomIndex]
&& oomAdj < DUMP_MEM_OOM_ADJ[oomIndex + 1])) {
oomPss[oomIndex] += myTotalPss;
oomSwapPss[oomIndex] += myTotalSwapPss;
if (oomProcs[oomIndex] == null) {
oomProcs[oomIndex] = new ArrayList<MemItem>();
}
oomProcs[oomIndex].add(pssItem);
oomRss[oomIndex] += myTotalRss;
break;
}
}
}
}
}
long nativeProcTotalPss = 0;
if (collectNative) {
mi = null;
synchronized (mProcessCpuTracker) {
final int N = mProcessCpuTracker.countStats();
for (int i=0; i<N; i++) {
ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
if (st.vsize > 0 && procMemsMap.indexOfKey(st.pid) < 0) {
if (mi == null) {
mi = new Debug.MemoryInfo();
}
if (!brief && !opts.oomOnly) {
if (!Debug.getMemoryInfo(st.pid, mi)) {
continue;
}
} else {
long pss = Debug.getPss(st.pid, tmpLong, null);
if (pss == 0) {
continue;
}
mi.nativePss = (int) pss;
mi.nativePrivateDirty = (int) tmpLong[0];
mi.nativeRss = (int) tmpLong[2];
}
final long myTotalPss = mi.getTotalPss();
final long myTotalSwapPss = mi.getTotalSwappedOutPss();
final long myTotalRss = mi.getTotalRss();
totalPss += myTotalPss;
totalSwapPss += myTotalSwapPss;
totalRss += myTotalRss;
nativeProcTotalPss += myTotalPss;
MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
st.name, myTotalPss, mi.getSummaryTotalSwapPss(), myTotalRss,
st.pid, false);
procMems.add(pssItem);
nativePss += mi.nativePss;
nativeSwapPss += mi.nativeSwappedOutPss;
nativeRss += mi.nativeRss;
dalvikPss += mi.dalvikPss;
dalvikSwapPss += mi.dalvikSwappedOutPss;
dalvikRss += mi.dalvikRss;
for (int j=0; j<dalvikSubitemPss.length; j++) {
dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
dalvikSubitemSwapPss[j] +=
mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
dalvikSubitemRss[j] += mi.getOtherRss(Debug.MemoryInfo.NUM_OTHER_STATS
+ j);
}
otherPss += mi.otherPss;
otherSwapPss += mi.otherSwappedOutPss;
otherRss += mi.otherRss;
for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
long mem = mi.getOtherPss(j);
miscPss[j] += mem;
otherPss -= mem;
mem = mi.getOtherSwappedOutPss(j);
miscSwapPss[j] += mem;
otherSwapPss -= mem;
mem = mi.getOtherRss(j);
miscRss[j] += mem;
otherRss -= mem;
}
oomPss[0] += myTotalPss;
oomSwapPss[0] += myTotalSwapPss;
if (oomProcs[0] == null) {
oomProcs[0] = new ArrayList<MemItem>();
}
oomProcs[0].add(pssItem);
oomRss[0] += myTotalRss;
}
}
}
ArrayList<MemItem> catMems = new ArrayList<MemItem>();
catMems.add(new MemItem("Native", "Native", nativePss, nativeSwapPss, nativeRss, -1));
final int dalvikId = -2;
catMems.add(new MemItem("Dalvik", "Dalvik", dalvikPss, dalvikSwapPss, dalvikRss,
dalvikId));
catMems.add(new MemItem("Unknown", "Unknown", otherPss, otherSwapPss, otherRss, -3));
for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
String label = Debug.MemoryInfo.getOtherLabel(j);
catMems.add(new MemItem(label, label, miscPss[j], miscSwapPss[j], miscRss[j], j));
}
if (dalvikSubitemPss.length > 0) {
// Add dalvik subitems.
for (MemItem memItem : catMems) {
int memItemStart = 0, memItemEnd = 0;
if (memItem.id == dalvikId) {
memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_START;
memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_END;
} else if (memItem.id == Debug.MemoryInfo.OTHER_DALVIK_OTHER) {
memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_OTHER_START;
memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_OTHER_END;
} else if (memItem.id == Debug.MemoryInfo.OTHER_DEX) {
memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_DEX_START;
memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_DEX_END;
} else if (memItem.id == Debug.MemoryInfo.OTHER_ART) {
memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_ART_START;
memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_ART_END;
} else {
continue; // No subitems, continue.
}
memItem.subitems = new ArrayList<MemItem>();
for (int j=memItemStart; j<=memItemEnd; j++) {
final String name = Debug.MemoryInfo.getOtherLabel(
Debug.MemoryInfo.NUM_OTHER_STATS + j);
memItem.subitems.add(new MemItem(name, name, dalvikSubitemPss[j],
dalvikSubitemSwapPss[j], dalvikSubitemRss[j], j));
}
}
}
ArrayList<MemItem> oomMems = new ArrayList<MemItem>();
for (int j=0; j<oomPss.length; j++) {
if (oomPss[j] != 0) {
String label = opts.isCompact ? DUMP_MEM_OOM_COMPACT_LABEL[j]
: DUMP_MEM_OOM_LABEL[j];
MemItem item = new MemItem(label, label, oomPss[j], oomSwapPss[j], oomRss[j],
DUMP_MEM_OOM_ADJ[j]);
item.subitems = oomProcs[j];
oomMems.add(item);
}
}
if (!opts.isCompact) {
pw.println();
}
if (!brief && !opts.oomOnly && !opts.isCompact) {
pw.println();
pw.println("Total RSS by process:");
dumpMemItems(pw, " ", "proc", procMems, true, opts.isCompact, false, false);
pw.println();
}
if (!opts.isCompact) {
pw.println("Total RSS by OOM adjustment:");
}
dumpMemItems(pw, " ", "oom", oomMems, false, opts.isCompact, false, false);
if (!brief && !opts.oomOnly) {
PrintWriter out = categoryPw != null ? categoryPw : pw;
if (!opts.isCompact) {
out.println();
out.println("Total RSS by category:");
}
dumpMemItems(out, " ", "cat", catMems, true, opts.isCompact, false, false);
}
opts.dumpSwapPss = opts.dumpSwapPss && hasSwapPss && totalSwapPss != 0;
if (!brief && !opts.oomOnly && !opts.isCompact) {
pw.println();
pw.println("Total PSS by process:");
dumpMemItems(pw, " ", "proc", procMems, true, opts.isCompact, true,
opts.dumpSwapPss);
pw.println();
}
if (!opts.isCompact) {
pw.println("Total PSS by OOM adjustment:");
}
dumpMemItems(pw, " ", "oom", oomMems, false, opts.isCompact, true, opts.dumpSwapPss);
if (!brief && !opts.oomOnly) {
PrintWriter out = categoryPw != null ? categoryPw : pw;
if (!opts.isCompact) {
out.println();
out.println("Total PSS by category:");
}
dumpMemItems(out, " ", "cat", catMems, true, opts.isCompact, true,
opts.dumpSwapPss);
}
if (!opts.isCompact) {
pw.println();
}
MemInfoReader memInfo = new MemInfoReader();
memInfo.readMemInfo();
if (nativeProcTotalPss > 0) {
synchronized (this) {
final long cachedKb = memInfo.getCachedSizeKb();
final long freeKb = memInfo.getFreeSizeKb();
final long zramKb = memInfo.getZramTotalSizeKb();
final long kernelKb = memInfo.getKernelUsedSizeKb();
EventLogTags.writeAmMeminfo(cachedKb*1024, freeKb*1024, zramKb*1024,
kernelKb*1024, nativeProcTotalPss*1024);
mProcessStats.addSysMemUsageLocked(cachedKb, freeKb, zramKb, kernelKb,
nativeProcTotalPss);
}
}
if (!brief) {
if (!opts.isCompact) {
pw.print("Total RAM: "); pw.print(stringifyKBSize(memInfo.getTotalSizeKb()));
pw.print(" (status ");
switch (mLastMemoryLevel) {
case ProcessStats.ADJ_MEM_FACTOR_NORMAL:
pw.println("normal)");
break;
case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
pw.println("moderate)");
break;
case ProcessStats.ADJ_MEM_FACTOR_LOW:
pw.println("low)");
break;
case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
pw.println("critical)");
break;
default:
pw.print(mLastMemoryLevel);
pw.println(")");
break;
}
pw.print(" Free RAM: ");
pw.print(stringifyKBSize(cachedPss + memInfo.getCachedSizeKb()
+ memInfo.getFreeSizeKb()));
pw.print(" (");
pw.print(stringifyKBSize(cachedPss));
pw.print(" cached pss + ");
pw.print(stringifyKBSize(memInfo.getCachedSizeKb()));
pw.print(" cached kernel + ");
pw.print(stringifyKBSize(memInfo.getFreeSizeKb()));
pw.println(" free)");
} else {
pw.print("ram,"); pw.print(memInfo.getTotalSizeKb()); pw.print(",");
pw.print(cachedPss + memInfo.getCachedSizeKb()
+ memInfo.getFreeSizeKb()); pw.print(",");
pw.println(totalPss - cachedPss);
}
}
long kernelUsed = memInfo.getKernelUsedSizeKb();
final long ionHeap = Debug.getIonHeapsSizeKb();
if (ionHeap > 0) {
final long ionMapped = Debug.getIonMappedSizeKb();
final long ionUnmapped = ionHeap - ionMapped;
final long ionPool = Debug.getIonPoolsSizeKb();
pw.print(" ION: ");
pw.print(stringifyKBSize(ionHeap + ionPool));
pw.print(" (");
pw.print(stringifyKBSize(ionMapped));
pw.print(" mapped + ");
pw.print(stringifyKBSize(ionUnmapped));
pw.print(" unmapped + ");
pw.print(stringifyKBSize(ionPool));
pw.println(" pools)");
// Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being
// set on ION VMAs, therefore consider the entire ION heap as used kernel memory
kernelUsed += ionHeap;
}
final long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
- memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
- kernelUsed - memInfo.getZramTotalSizeKb();
if (!opts.isCompact) {
pw.print(" Used RAM: "); pw.print(stringifyKBSize(totalPss - cachedPss
+ kernelUsed)); pw.print(" (");
pw.print(stringifyKBSize(totalPss - cachedPss)); pw.print(" used pss + ");
pw.print(stringifyKBSize(kernelUsed)); pw.print(" kernel)\n");
pw.print(" Lost RAM: "); pw.println(stringifyKBSize(lostRAM));
} else {
pw.print("lostram,"); pw.println(lostRAM);
}
if (!brief) {
if (memInfo.getZramTotalSizeKb() != 0) {
if (!opts.isCompact) {
pw.print(" ZRAM: ");
pw.print(stringifyKBSize(memInfo.getZramTotalSizeKb()));
pw.print(" physical used for ");
pw.print(stringifyKBSize(memInfo.getSwapTotalSizeKb()
- memInfo.getSwapFreeSizeKb()));
pw.print(" in swap (");
pw.print(stringifyKBSize(memInfo.getSwapTotalSizeKb()));
pw.println(" total swap)");
} else {
pw.print("zram,"); pw.print(memInfo.getZramTotalSizeKb()); pw.print(",");
pw.print(memInfo.getSwapTotalSizeKb()); pw.print(",");
pw.println(memInfo.getSwapFreeSizeKb());
}
}
final long[] ksm = getKsmInfo();
if (!opts.isCompact) {
if (ksm[KSM_SHARING] != 0 || ksm[KSM_SHARED] != 0 || ksm[KSM_UNSHARED] != 0
|| ksm[KSM_VOLATILE] != 0) {
pw.print(" KSM: "); pw.print(stringifyKBSize(ksm[KSM_SHARING]));
pw.print(" saved from shared ");
pw.print(stringifyKBSize(ksm[KSM_SHARED]));
pw.print(" "); pw.print(stringifyKBSize(ksm[KSM_UNSHARED]));
pw.print(" unshared; ");
pw.print(stringifyKBSize(
ksm[KSM_VOLATILE])); pw.println(" volatile");
}
pw.print(" Tuning: ");
pw.print(ActivityManager.staticGetMemoryClass());
pw.print(" (large ");
pw.print(ActivityManager.staticGetLargeMemoryClass());
pw.print("), oom ");
pw.print(stringifySize(
mProcessList.getMemLevel(ProcessList.CACHED_APP_MAX_ADJ), 1024));
pw.print(", restore limit ");
pw.print(stringifyKBSize(mProcessList.getCachedRestoreThresholdKb()));
if (ActivityManager.isLowRamDeviceStatic()) {
pw.print(" (low-ram)");
}
if (ActivityManager.isHighEndGfx()) {
pw.print(" (high-end-gfx)");
}
pw.println();
} else {
pw.print("ksm,"); pw.print(ksm[KSM_SHARING]); pw.print(",");
pw.print(ksm[KSM_SHARED]); pw.print(","); pw.print(ksm[KSM_UNSHARED]);
pw.print(","); pw.println(ksm[KSM_VOLATILE]);
pw.print("tuning,");
pw.print(ActivityManager.staticGetMemoryClass());
pw.print(',');
pw.print(ActivityManager.staticGetLargeMemoryClass());
pw.print(',');
pw.print(mProcessList.getMemLevel(ProcessList.CACHED_APP_MAX_ADJ)/1024);
if (ActivityManager.isLowRamDeviceStatic()) {
pw.print(",low-ram");
}
if (ActivityManager.isHighEndGfx()) {
pw.print(",high-end-gfx");
}
pw.println();
}
}
}
}
三、Debug.getMemoryInfo
关键的统计内存方法,Graphics+smaps
3.1 -> frameworks/base/core/java/android/os/Debug.java
public static native boolean getMemoryInfo(int pid, MemoryInfo memoryInfo);
3.2 -> frameworks/base/core/jni/android_os_Debug.cpp
static const JNINativeMethod gMethods[] = {
...
{ "getMemoryInfo", "(ILandroid/os/Debug$MemoryInfo;)Z",
(void*) android_os_Debug_getDirtyPagesPid },
...
};
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));
// 读取 smaps 到 stats 中 转到 3.3
// 此处将 smaps 文件信息保存到 stats 中
if (!load_maps(pid, stats, &foundSwapPss)) {
return JNI_FALSE;
}
// 获取 Graphics 内存相关信息
struct graphics_memory_pss graphics_mem;
// 转到 3.4,根据 pid 获取 Graphic 内存,赋值到 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;
}
// 根据 load_smaps 解析的数据进行赋值到 stats[HEAP_UNKNOWN]中进行汇总,此处为枚举类型 HEAP_DALVIK_OTHER 到 HEAP_OTHER_MEMTRACK 之间的类型,详见 3.3.1
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;
}
// 使用 stats 的值对 JNI 传过来的 Java 对象进行赋值 HEAP_UNKNOWN、HEAP_DALVIK、HEAP_NATIVE
for (int i=0; i<_NUM_CORE_HEAP; i++) {
env->SetIntField(object, stat_fields[i].pss_field, stats[i].pss);// Java
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);
}
// foundSwapPss 在 load_smaps 判断 swap_pss 是否大于 0
env->SetBooleanField(object, hasSwappedOutPss_field, foundSwapPss);
// 对 Java 中 Meminfo对象 otherStats field 进行赋值
jintArray otherIntArray = (jintArray)env->GetObjectField(object, otherStats_field);
// 获取 Java 对象 otherStats 指针
jint* otherArray = (jint*)env->GetPrimitiveArrayCritical(otherIntArray, 0);
if (otherArray == NULL) {
return JNI_FALSE;
}
int j=0;
// 这里 Java otherStats 对象是一个 int[32*9],用一个数组对象进行存储多个数据
// 对应循化也是 _NUM_HEAP-_NUM_CORE_HEAP = 32,
// pss、... swappedOutPss 9项,
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;
}
// 对 Java 对象 otherStats 指针的操作进行 release
env->ReleasePrimitiveArrayCritical(otherIntArray, otherArray, 0);
return JNI_TRUE;
}
3.3 -> frameworks/base/core/jni/android_os_Debug.cpp#load_maps
关键的数据读取操作,这里关系到 linux 内存管理的 smaps。
static bool load_maps(int pid, stats_t* stats, bool* foundSwapPss)
{
*foundSwapPss = false;
uint64_t prev_end = 0;
int prev_heap = HEAP_UNKNOWN;
// 读取 /proc/<进程 pid>/smaps 文件
std::string smaps_path = base::StringPrintf("/proc/%d/smaps", pid);
auto vma_scan = [&](const meminfo::Vma& vma) {
int which_heap = HEAP_UNKNOWN;
int sub_heap = HEAP_UNKNOWN;
bool is_swappable = false;
std::string name;
if (base::EndsWith(vma.name, " (deleted)")) {
name = vma.name.substr(0, vma.name.size() - strlen(" (deleted)"));
} else {
name = vma.name;
}
uint32_t namesz = name.size();
// 将smaps 文件的 path [heap]、[anon:libc_malloc]、[anon:scudo:、[anon:GWP-ASan、归为 Heap_Native
// 同理其他的相关内容进行累加,保存到 stats 中,最终会被输出,如 .so、.jar 等
// 类型使用一个枚举进行维护的,转到 3.3.1
if (base::StartsWith(name, "[heap]")) {
which_heap = HEAP_NATIVE;
} else if (base::StartsWith(name, "[anon:libc_malloc]")) {
which_heap = HEAP_NATIVE;
} else if (base::StartsWith(name, "[anon:scudo:")) {
which_heap = HEAP_NATIVE;
} else if (base::StartsWith(name, "[anon:GWP-ASan")) {
which_heap = HEAP_NATIVE;
} else if (base::StartsWith(name, "[stack")) {
which_heap = HEAP_STACK;
} else if (base::StartsWith(name, "[anon:stack_and_tls:")) {
which_heap = HEAP_STACK;
} else if (base::EndsWith(name, ".so")) {
which_heap = HEAP_SO;
is_swappable = true;
} else if (base::EndsWith(name, ".jar")) {
which_heap = HEAP_JAR;
is_swappable = true;
} else if (base::EndsWith(name, ".apk")) {
which_heap = HEAP_APK;
is_swappable = true;
} else if (base::EndsWith(name, ".ttf")) {
which_heap = HEAP_TTF;
is_swappable = true;
} else if ((base::EndsWith(name, ".odex")) ||
(namesz > 4 && strstr(name.c_str(), ".dex") != nullptr)) {
which_heap = HEAP_DEX;
sub_heap = HEAP_DEX_APP_DEX;
is_swappable = true;
} else if (base::EndsWith(name, ".vdex")) {
which_heap = HEAP_DEX;
// Handle system@framework@boot and system/framework/boot|apex
if ((strstr(name.c_str(), "@boot") != nullptr) ||
(strstr(name.c_str(), "/boot") != nullptr) ||
(strstr(name.c_str(), "/apex") != nullptr)) {
sub_heap = HEAP_DEX_BOOT_VDEX;
} else {
sub_heap = HEAP_DEX_APP_VDEX;
}
is_swappable = true;
} else if (base::EndsWith(name, ".oat")) {
which_heap = HEAP_OAT;
is_swappable = true;
} else if (base::EndsWith(name, ".art") || base::EndsWith(name, ".art]")) {
which_heap = HEAP_ART;
// Handle system@framework@boot* and system/framework/boot|apex*
if ((strstr(name.c_str(), "@boot") != nullptr) ||
(strstr(name.c_str(), "/boot") != nullptr) ||
(strstr(name.c_str(), "/apex") != nullptr)) {
sub_heap = HEAP_ART_BOOT;
} else {
sub_heap = HEAP_ART_APP;
}
is_swappable = true;
} else if (base::StartsWith(name, "/dev/")) {
which_heap = HEAP_UNKNOWN_DEV;
if (base::StartsWith(name, "/dev/kgsl-3d0")) {
which_heap = HEAP_GL_DEV;
} else if (base::StartsWith(name, "/dev/ashmem/CursorWindow")) {
which_heap = HEAP_CURSOR;
} else if (base::StartsWith(name, "/dev/ashmem/jit-zygote-cache")) {
which_heap = HEAP_DALVIK_OTHER;
sub_heap = HEAP_DALVIK_OTHER_ZYGOTE_CODE_CACHE;
} else if (base::StartsWith(name, "/dev/ashmem")) {
which_heap = HEAP_ASHMEM;
}
} else if (base::StartsWith(name, "/memfd:jit-cache")) {
which_heap = HEAP_DALVIK_OTHER;
sub_heap = HEAP_DALVIK_OTHER_APP_CODE_CACHE;
} else if (base::StartsWith(name, "/memfd:jit-zygote-cache")) {
which_heap = HEAP_DALVIK_OTHER;
sub_heap = HEAP_DALVIK_OTHER_ZYGOTE_CODE_CACHE;
} else if (base::StartsWith(name, "[anon:")) {
which_heap = HEAP_UNKNOWN;
if (base::StartsWith(name, "[anon:dalvik-")) {
which_heap = HEAP_DALVIK_OTHER;
if (base::StartsWith(name, "[anon:dalvik-LinearAlloc")) {
sub_heap = HEAP_DALVIK_OTHER_LINEARALLOC;
} else if (base::StartsWith(name, "[anon:dalvik-alloc space") ||
base::StartsWith(name, "[anon:dalvik-main space")) {
// This is the regular Dalvik heap.
which_heap = HEAP_DALVIK;
sub_heap = HEAP_DALVIK_NORMAL;
} else if (base::StartsWith(name,
"[anon:dalvik-large object space") ||
base::StartsWith(
name, "[anon:dalvik-free list large object space")) {
which_heap = HEAP_DALVIK;
sub_heap = HEAP_DALVIK_LARGE;
} else if (base::StartsWith(name, "[anon:dalvik-non moving space")) {
which_heap = HEAP_DALVIK;
sub_heap = HEAP_DALVIK_NON_MOVING;
} else if (base::StartsWith(name, "[anon:dalvik-zygote space")) {
which_heap = HEAP_DALVIK;
sub_heap = HEAP_DALVIK_ZYGOTE;
} else if (base::StartsWith(name, "[anon:dalvik-indirect ref")) {
sub_heap = HEAP_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE;
} else if (base::StartsWith(name, "[anon:dalvik-jit-code-cache") ||
base::StartsWith(name, "[anon:dalvik-data-code-cache")) {
sub_heap = HEAP_DALVIK_OTHER_APP_CODE_CACHE;
} else if (base::StartsWith(name, "[anon:dalvik-CompilerMetadata")) {
sub_heap = HEAP_DALVIK_OTHER_COMPILER_METADATA;
} else {
sub_heap = HEAP_DALVIK_OTHER_ACCOUNTING; // Default to accounting.
}
}
} else if (namesz > 0) {
which_heap = HEAP_UNKNOWN_MAP;
} else if (vma.start == prev_end && prev_heap == HEAP_SO) {
// bss section of a shared library
which_heap = HEAP_SO;
}
prev_end = vma.end;
prev_heap = which_heap;
const meminfo::MemUsage& usage = vma.usage;
if (usage.swap_pss > 0 && *foundSwapPss != true) {
*foundSwapPss = true;
}
uint64_t swapable_pss = 0;
if (is_swappable && (usage.pss > 0)) {
float sharing_proportion = 0.0;
if ((usage.shared_clean > 0) || (usage.shared_dirty > 0)) {
sharing_proportion = (usage.pss - usage.uss) / (usage.shared_clean + usage.shared_dirty);
}
swapable_pss = (sharing_proportion * usage.shared_clean) + usage.private_clean;
}
// 针对不同的内存类型进行汇总,累加计算
stats[which_heap].pss += usage.pss;
stats[which_heap].swappablePss += swapable_pss;
stats[which_heap].rss += usage.rss;
stats[which_heap].privateDirty += usage.private_dirty;
stats[which_heap].sharedDirty += usage.shared_dirty;
stats[which_heap].privateClean += usage.private_clean;
stats[which_heap].sharedClean += usage.shared_clean;
stats[which_heap].swappedOut += usage.swap;
stats[which_heap].swappedOutPss += usage.swap_pss;
if (which_heap == HEAP_DALVIK || which_heap == HEAP_DALVIK_OTHER ||
which_heap == HEAP_DEX || which_heap == HEAP_ART) {
stats[sub_heap].pss += usage.pss;
stats[sub_heap].swappablePss += swapable_pss;
stats[sub_heap].rss += usage.rss;
stats[sub_heap].privateDirty += usage.private_dirty;
stats[sub_heap].sharedDirty += usage.shared_dirty;
stats[sub_heap].privateClean += usage.private_clean;
stats[sub_heap].sharedClean += usage.shared_clean;
stats[sub_heap].swappedOut += usage.swap;
stats[sub_heap].swappedOutPss += usage.swap_pss;
}
};
return meminfo::ForEachVmaFromFile(smaps_path, vma_scan);
}
3.3.1 -> frameworks/base/core/jni/android_os_Debug.cpp
smaps 文件内存归类
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,
// Dalvik extra sections (heap).
HEAP_DALVIK_NORMAL,
HEAP_DALVIK_LARGE,
HEAP_DALVIK_ZYGOTE,
HEAP_DALVIK_NON_MOVING,
// Dalvik other extra sections.
HEAP_DALVIK_OTHER_LINEARALLOC,
HEAP_DALVIK_OTHER_ACCOUNTING,
HEAP_DALVIK_OTHER_ZYGOTE_CODE_CACHE,
HEAP_DALVIK_OTHER_APP_CODE_CACHE,
HEAP_DALVIK_OTHER_COMPILER_METADATA,
HEAP_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE,
// Boot vdex / app dex / app vdex
HEAP_DEX_BOOT_VDEX,
HEAP_DEX_APP_DEX,
HEAP_DEX_APP_VDEX,
// App art, boot art.
HEAP_ART_APP,
HEAP_ART_BOOT,
_NUM_HEAP,
_NUM_EXCLUSIVE_HEAP = HEAP_OTHER_MEMTRACK+1,
_NUM_CORE_HEAP = HEAP_NATIVE+1
};
3.4 -> frameworks/base/core/jni/android_os_Debug.cpp#read_memtrack_memory
获取 Graphics 内存
static int read_memtrack_memory(int pid, struct graphics_memory_pss* graphics_mem)
{
// 构造一个 memtrack_proc
struct memtrack_proc* p = memtrack_proc_new();
if (p == NULL) {
ALOGW("failed to create memtrack_proc");
return -1;
}
// 将指定 pid 的值通过 p(libmemtrack) 赋值给 graphics_mem ,转到 3.4.1
int err = read_memtrack_memory(p, pid, graphics_mem);
// 释放内存
memtrack_proc_destroy(p);
return err;
}
3.4.1-> frameworks/base/core/jni/android_os_Debug.cpp#read_memtrack_memory
根据 libmemtrack 获取进程正在使用的 Graphics 内存,在 smaps 文件中统计的 graphics 都不包含此部分
static int read_memtrack_memory(struct memtrack_proc* p, int pid,
struct graphics_memory_pss* graphics_mem)
{
// 通过 HAL 获取,需要进行可用判断
int err = memtrack_proc_get(p, pid);
if (err != 0) {
// The memtrack HAL may not be available, do not log to avoid flooding
// logcat.
return err;
}
// 获取 graphics pss (MemtrackType::GRAPHICS)
ssize_t pss = memtrack_proc_graphics_pss(p);
if (pss < 0) {
ALOGW("failed to get graphics pss: %zd", pss);
return pss;
}
graphics_mem->graphics = pss / 1024;
// 获取 gl pss (MemtrackType::GL)
pss = memtrack_proc_gl_pss(p);
if (pss < 0) {
ALOGW("failed to get gl pss: %zd", pss);
return pss;
}
graphics_mem->gl = pss / 1024;
// 获取其他 pss (MemtrackType::MULTIMEDIA,MemtrackType::CAMERA, MemtrackType::OTHER)
pss = memtrack_proc_other_pss(p);
if (pss < 0) {
ALOGW("failed to get other pss: %zd", pss);
return pss;
}
graphics_mem->other = pss / 1024;
return 0;
}
四、目标进程 dumpMemInfo
4.1-> frameworks/base/core/java/android/app/ActivityThread.java#ApplicationThread#dumpMemInfo
system_server 进程通过 binder 调用进入目标 pid 进程
@Override
public void dumpMemInfo(ParcelFileDescriptor pfd, Debug.MemoryInfo mem, boolean checkin,
boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly,
boolean dumpUnreachable, String[] args) {
FileOutputStream fout = new FileOutputStream(pfd.getFileDescriptor());
PrintWriter pw = new FastPrintWriter(fout);
try {
// 转到 4.2 -> frameworks/base/core/java/android/app/ActivityThread.java#ApplicationThread#dumpMemInfo
dumpMemInfo(pw, mem, checkin, dumpFullInfo, dumpDalvik, dumpSummaryOnly, dumpUnreachable);
} finally {
pw.flush();
IoUtils.closeQuietly(pfd);
}
}
4.2 -> frameworks/base/core/java/android/app/ActivityThread.java#ApplicationThread#dumpMemInfo
这里就是最终的输出部分,我们直接看重要方法
private void dumpMemInfo(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin,
boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly, boolean dumpUnreachable) {
// 计算除了 MemInfo 以外的数据
// native Heap相关
long nativeMax = Debug.getNativeHeapSize() / 1024;
long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
// Java 相关
Runtime runtime = Runtime.getRuntime();
runtime.gc(); // Do GC since countInstancesOfClass counts unreachable objects.
long dalvikMax = runtime.totalMemory() / 1024;
long dalvikFree = runtime.freeMemory() / 1024;
long dalvikAllocated = dalvikMax - dalvikFree;
Class[] classesToCount = new Class[] {
ContextImpl.class,
Activity.class,
WebView.class,
OpenSSLSocketImpl.class
};
// 需要输出对象相关内容
long[] instanceCounts = VMDebug.countInstancesOfClasses(classesToCount, true);
long appContextInstanceCount = instanceCounts[0];
long activityInstanceCount = instanceCounts[1];
long webviewInstanceCount = instanceCounts[2];
long openSslSocketCount = instanceCounts[3];
long viewInstanceCount = ViewDebug.getViewInstanceCount();
long viewRootInstanceCount = ViewDebug.getViewRootImplCount();
int globalAssetCount = AssetManager.getGlobalAssetCount();
int globalAssetManagerCount = AssetManager.getGlobalAssetManagerCount();
int binderLocalObjectCount = Debug.getBinderLocalObjectCount();
int binderProxyObjectCount = Debug.getBinderProxyObjectCount();
int binderDeathObjectCount = Debug.getBinderDeathObjectCount();
long parcelSize = Parcel.getGlobalAllocSize();
long parcelCount = Parcel.getGlobalAllocCount();
SQLiteDebug.PagerStats stats = SQLiteDebug.getDatabaseInfo();
// 关键的输出方法
// 转到 4.3 -> frameworks/base/core/java/android/app/ActivityThread.java#ApplicationThread#dumpMemInfoTable
dumpMemInfoTable(pw, memInfo, checkin, dumpFullInfo, dumpDalvik, dumpSummaryOnly,
Process.myPid(),
(mBoundApplication != null) ? mBoundApplication.processName : "unknown",
nativeMax, nativeAllocated, nativeFree,
dalvikMax, dalvikAllocated, dalvikFree);
if (checkin) {
...
return;
}
// 输出
pw.println(" ");
pw.println(" Objects");
printRow(pw, TWO_COUNT_COLUMNS, "Views:", viewInstanceCount, "ViewRootImpl:",
viewRootInstanceCount);
printRow(pw, TWO_COUNT_COLUMNS, "AppContexts:", appContextInstanceCount,
"Activities:", activityInstanceCount);
printRow(pw, TWO_COUNT_COLUMNS, "Assets:", globalAssetCount,
"AssetManagers:", globalAssetManagerCount);
printRow(pw, TWO_COUNT_COLUMNS, "Local Binders:", binderLocalObjectCount,
"Proxy Binders:", binderProxyObjectCount);
printRow(pw, TWO_COUNT_COLUMNS, "Parcel memory:", parcelSize/1024,
"Parcel count:", parcelCount);
printRow(pw, TWO_COUNT_COLUMNS, "Death Recipients:", binderDeathObjectCount,
"OpenSSL Sockets:", openSslSocketCount);
printRow(pw, ONE_COUNT_COLUMN, "WebViews:", webviewInstanceCount);
// SQLite mem info
pw.println(" ");
pw.println(" SQL");
printRow(pw, ONE_COUNT_COLUMN, "MEMORY_USED:", stats.memoryUsed / 1024);
printRow(pw, TWO_COUNT_COLUMNS, "PAGECACHE_OVERFLOW:",
stats.pageCacheOverflow / 1024, "MALLOC_SIZE:", stats.largestMemAlloc / 1024);
pw.println(" ");
int N = stats.dbStats.size();
if (N > 0) {
pw.println(" DATABASES");
printRow(pw, DB_INFO_FORMAT, "pgsz", "dbsz", "Lookaside(b)", "cache",
"Dbname");
for (int i = 0; i < N; i++) {
DbStats dbStats = stats.dbStats.get(i);
printRow(pw, DB_INFO_FORMAT,
(dbStats.pageSize > 0) ? String.valueOf(dbStats.pageSize) : " ",
(dbStats.dbSize > 0) ? String.valueOf(dbStats.dbSize) : " ",
(dbStats.lookaside > 0) ? String.valueOf(dbStats.lookaside) : " ",
dbStats.cache, dbStats.dbName);
}
}
// Asset details.
String assetAlloc = AssetManager.getAssetAllocations();
if (assetAlloc != null) {
pw.println(" ");
pw.println(" Asset Allocations");
pw.print(assetAlloc);
}
// Unreachable native memory
if (dumpUnreachable) {
boolean showContents = ((mBoundApplication != null)
&& ((mBoundApplication.appInfo.flags&ApplicationInfo.FLAG_DEBUGGABLE) != 0))
|| android.os.Build.IS_DEBUGGABLE;
pw.println(" ");
pw.println(" Unreachable memory");
pw.print(Debug.getUnreachableMemory(100, showContents));
}
}
4.3 -> frameworks/base/core/java/android/app/ActivityThread.java#ApplicationThread#dumpMemInfoTable
既然数据都已经有了,接下来就可以进行输出了,此处是输出的详细内容
public static void dumpMemInfoTable(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin,
boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly,
int pid, String processName,
long nativeMax, long nativeAllocated, long nativeFree,
long dalvikMax, long dalvikAllocated, long dalvikFree) {
// dump 命令 --checkin 参数
if (checkin) {
// 输出各参数详细信息
...
}
// dumpsys meminfo 命令 -s 参数
if (!dumpSummaryOnly) {
// dumpsys meminfo 命令 -a 参数
if (dumpFullInfo) {
...
} else {
// 进行第一部分输出,表头
printRow(pw, HEAP_COLUMN, "", "Pss", "Private",
"Private", memInfo.hasSwappedOutPss ? "SwapPss" : "Swap",
"Rss", "Heap", "Heap", "Heap");
printRow(pw, HEAP_COLUMN, "", "Total", "Dirty",
"Clean", "Dirty", "Total", "Size", "Alloc", "Free");
printRow(pw, HEAP_COLUMN, "", "------", "------", "------",
"------", "------", "------", "------", "------", "------");
// Native Heap
printRow(pw, HEAP_COLUMN, "Native Heap", memInfo.nativePss,
memInfo.nativePrivateDirty,
memInfo.nativePrivateClean,
memInfo.hasSwappedOutPss ? memInfo.nativeSwappedOutPss :
memInfo.nativeSwappedOut, memInfo.nativeRss,
nativeMax, nativeAllocated, nativeFree);
// Dalvik Heap
printRow(pw, HEAP_COLUMN, "Dalvik Heap", memInfo.dalvikPss,
memInfo.dalvikPrivateDirty,
memInfo.dalvikPrivateClean,
memInfo.hasSwappedOutPss ? memInfo.dalvikSwappedOutPss :
memInfo.dalvikSwappedOut, memInfo.dalvikRss,
dalvikMax, dalvikAllocated, dalvikFree);
}
int otherPss = memInfo.otherPss;
int otherSwappablePss = memInfo.otherSwappablePss;
int otherSharedDirty = memInfo.otherSharedDirty;
int otherPrivateDirty = memInfo.otherPrivateDirty;
int otherSharedClean = memInfo.otherSharedClean;
int otherPrivateClean = memInfo.otherPrivateClean;
int otherSwappedOut = memInfo.otherSwappedOut;
int otherSwappedOutPss = memInfo.otherSwappedOutPss;
int otherRss = memInfo.otherRss;
// 各种其余信息,比如 .so mmap .jar mmap
for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) {
final int myPss = memInfo.getOtherPss(i);
final int mySwappablePss = memInfo.getOtherSwappablePss(i);
final int mySharedDirty = memInfo.getOtherSharedDirty(i);
final int myPrivateDirty = memInfo.getOtherPrivateDirty(i);
final int mySharedClean = memInfo.getOtherSharedClean(i);
final int myPrivateClean = memInfo.getOtherPrivateClean(i);
final int mySwappedOut = memInfo.getOtherSwappedOut(i);
final int mySwappedOutPss = memInfo.getOtherSwappedOutPss(i);
final int myRss = memInfo.getOtherRss(i);
if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
|| mySharedClean != 0 || myPrivateClean != 0 || myRss != 0
|| (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) {
if (dumpFullInfo) {
printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
mySharedClean, myPrivateClean,
memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut,
myRss, "", "", "");
} else {
printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
myPss, myPrivateDirty,
myPrivateClean,
memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut,
myRss, "", "", "");
}
otherPss -= myPss;
otherSwappablePss -= mySwappablePss;
otherSharedDirty -= mySharedDirty;
otherPrivateDirty -= myPrivateDirty;
otherSharedClean -= mySharedClean;
otherPrivateClean -= myPrivateClean;
otherSwappedOut -= mySwappedOut;
otherSwappedOutPss -= mySwappedOutPss;
otherRss -= myRss;
}
}
// dumpsys meminfo 命令 -a 参数
if (dumpFullInfo) {
...
} else {
// Unknown
printRow(pw, HEAP_COLUMN, "Unknown", otherPss,
otherPrivateDirty, otherPrivateClean,
memInfo.hasSwappedOutPss ? otherSwappedOutPss : otherSwappedOut,
otherRss, "", "", "");
// TOTAL
printRow(pw, HEAP_COLUMN, "TOTAL", memInfo.getTotalPss(),
memInfo.getTotalPrivateDirty(),
memInfo.getTotalPrivateClean(),
memInfo.hasSwappedOutPss ? memInfo.getTotalSwappedOutPss() :
memInfo.getTotalSwappedOut(), memInfo.getTotalPss(),
nativeMax+dalvikMax,
nativeAllocated+dalvikAllocated, nativeFree+dalvikFree);
}
// dumpsys meminfo 命令 -a || -d参数
if (dumpDalvik) {
...
}
}
// 第二部分 App Summary,我们经常关注的
pw.println(" ");
pw.println(" App Summary");
// 表头
printRow(pw, TWO_COUNT_COLUMN_HEADER, "", "Pss(KB)", "", "Rss(KB)");
printRow(pw, TWO_COUNT_COLUMN_HEADER, "", "------", "", "------");
printRow(pw, TWO_COUNT_COLUMNS,
"Java Heap:", memInfo.getSummaryJavaHeap(), "", memInfo.getSummaryJavaHeapRss());
printRow(pw, TWO_COUNT_COLUMNS,
"Native Heap:", memInfo.getSummaryNativeHeap(), "",
memInfo.getSummaryNativeHeapRss());
printRow(pw, TWO_COUNT_COLUMNS,
"Code:", memInfo.getSummaryCode(), "", memInfo.getSummaryCodeRss());
printRow(pw, TWO_COUNT_COLUMNS,
"Stack:", memInfo.getSummaryStack(), "", memInfo.getSummaryStackRss());
printRow(pw, TWO_COUNT_COLUMNS,
"Graphics:", memInfo.getSummaryGraphics(), "", memInfo.getSummaryGraphicsRss());
printRow(pw, ONE_COUNT_COLUMN,
"Private Other:", memInfo.getSummaryPrivateOther());
printRow(pw, ONE_COUNT_COLUMN,
"System:", memInfo.getSummarySystem());
printRow(pw, ONE_ALT_COUNT_COLUMN,
"Unknown:", "", "", memInfo.getSummaryUnknownRss());
pw.println(" ");
if (memInfo.hasSwappedOutPss) {
printRow(pw, THREE_COUNT_COLUMNS,
"TOTAL PSS:", memInfo.getSummaryTotalPss(),
"TOTAL RSS:", memInfo.getTotalRss(),
"TOTAL SWAP PSS:", memInfo.getSummaryTotalSwapPss());
} else {
printRow(pw, THREE_COUNT_COLUMNS,
"TOTAL PSS:", memInfo.getSummaryTotalPss(),
"TOTAL RSS:", memInfo.getTotalRss(),
"TOTAL SWAP (KB):", memInfo.getSummaryTotalSwap());
}
}
附
附1 -> frameworks/base/core/java/android/os/Debug.java#MemoryInfo
该类记录了应用内存使用的数据,具体的赋值上文已经分析,在 Native 层解析 smaps 进行赋值。主要关注该类的局部变量含义,以及相关计算方法
TODO 相关局部变量说明
/**
* This class is used to retrieved various statistics about the memory mappings for this
* process. The returned info is broken down by dalvik, native, and other. All results are in kB.
*/
public static class MemoryInfo implements Parcelable {
/** The proportional set size for dalvik heap. (Doesn't include other Dalvik overhead.) */
public int dalvikPss;
/** The proportional set size that is swappable for dalvik heap. */
/** @hide We may want to expose this, eventually. */
@UnsupportedAppUsage
public int dalvikSwappablePss;
/** @hide The resident set size for dalvik heap. (Without other Dalvik overhead.) */
@UnsupportedAppUsage
public int dalvikRss;
/** The private dirty pages used by dalvik heap. */
public int dalvikPrivateDirty;
/** The shared dirty pages used by dalvik heap. */
public int dalvikSharedDirty;
/** The private clean pages used by dalvik heap. */
/** @hide We may want to expose this, eventually. */
@UnsupportedAppUsage
public int dalvikPrivateClean;
/** The shared clean pages used by dalvik heap. */
/** @hide We may want to expose this, eventually. */
@UnsupportedAppUsage
public int dalvikSharedClean;
/** The dirty dalvik pages that have been swapped out. */
/** @hide We may want to expose this, eventually. */
@UnsupportedAppUsage
public int dalvikSwappedOut;
/** The dirty dalvik pages that have been swapped out, proportional. */
/** @hide We may want to expose this, eventually. */
@UnsupportedAppUsage
public int dalvikSwappedOutPss;
/** The proportional set size for the native heap. */
public int nativePss;
/** The proportional set size that is swappable for the native heap. */
/** @hide We may want to expose this, eventually. */
@UnsupportedAppUsage
public int nativeSwappablePss;
/** @hide The resident set size for the native heap. */
@UnsupportedAppUsage
public int nativeRss;
/** The private dirty pages used by the native heap. */
public int nativePrivateDirty;
/** The shared dirty pages used by the native heap. */
public int nativeSharedDirty;
/** The private clean pages used by the native heap. */
/** @hide We may want to expose this, eventually. */
@UnsupportedAppUsage
public int nativePrivateClean;
/** The shared clean pages used by the native heap. */
/** @hide We may want to expose this, eventually. */
@UnsupportedAppUsage
public int nativeSharedClean;
/** The dirty native pages that have been swapped out. */
/** @hide We may want to expose this, eventually. */
@UnsupportedAppUsage
public int nativeSwappedOut;
/** The dirty native pages that have been swapped out, proportional. */
/** @hide We may want to expose this, eventually. */
@UnsupportedAppUsage
public int nativeSwappedOutPss;
/** The proportional set size for everything else. */
public int otherPss;
/** The proportional set size that is swappable for everything else. */
/** @hide We may want to expose this, eventually. */
@UnsupportedAppUsage
public int otherSwappablePss;
/** @hide The resident set size for everything else. */
@UnsupportedAppUsage
public int otherRss;
/** The private dirty pages used by everything else. */
public int otherPrivateDirty;
/** The shared dirty pages used by everything else. */
public int otherSharedDirty;
/** The private clean pages used by everything else. */
/** @hide We may want to expose this, eventually. */
@UnsupportedAppUsage
public int otherPrivateClean;
/** The shared clean pages used by everything else. */
/** @hide We may want to expose this, eventually. */
@UnsupportedAppUsage
public int otherSharedClean;
/** The dirty pages used by anyting else that have been swapped out. */
/** @hide We may want to expose this, eventually. */
@UnsupportedAppUsage
public int otherSwappedOut;
/** The dirty pages used by anyting else that have been swapped out, proportional. */
/** @hide We may want to expose this, eventually. */
@UnsupportedAppUsage
public int otherSwappedOutPss;
/** Whether the kernel reports proportional swap usage */
/** @hide */
@UnsupportedAppUsage
public boolean hasSwappedOutPss;
/** @hide */
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 */
public static final int OTHER_ASHMEM = 3;
/** @hide */
public static final int OTHER_GL_DEV = 4;
/** @hide */
public static final int OTHER_UNKNOWN_DEV = 5;
/** @hide */
public static final int OTHER_SO = 6;
/** @hide */
public static final int OTHER_JAR = 7;
/** @hide */
public static final int OTHER_APK = 8;
/** @hide */
public static final int OTHER_TTF = 9;
/** @hide */
public static final int OTHER_DEX = 10;
/** @hide */
public static final int OTHER_OAT = 11;
/** @hide */
public static final int OTHER_ART = 12;
/** @hide */
public static final int OTHER_UNKNOWN_MAP = 13;
/** @hide */
public static final int OTHER_GRAPHICS = 14;
/** @hide */
public static final int OTHER_GL = 15;
/** @hide */
public static final int OTHER_OTHER_MEMTRACK = 16;
// Needs to be declared here for the DVK_STAT ranges below.
/** @hide */
@UnsupportedAppUsage
public static final int NUM_OTHER_STATS = 17;
// Dalvik subsections.
/** @hide */
public static final int OTHER_DALVIK_NORMAL = 17;
/** @hide */
public static final int OTHER_DALVIK_LARGE = 18;
/** @hide */
public static final int OTHER_DALVIK_ZYGOTE = 19;
/** @hide */
public static final int OTHER_DALVIK_NON_MOVING = 20;
// Section begins and ends for dumpsys, relative to the DALVIK categories.
/** @hide */
public static final int OTHER_DVK_STAT_DALVIK_START =
OTHER_DALVIK_NORMAL - NUM_OTHER_STATS;
/** @hide */
public static final int OTHER_DVK_STAT_DALVIK_END =
OTHER_DALVIK_NON_MOVING - NUM_OTHER_STATS;
// Dalvik Other subsections.
/** @hide */
public static final int OTHER_DALVIK_OTHER_LINEARALLOC = 21;
/** @hide */
public static final int OTHER_DALVIK_OTHER_ACCOUNTING = 22;
/** @hide */
public static final int OTHER_DALVIK_OTHER_ZYGOTE_CODE_CACHE = 23;
/** @hide */
public static final int OTHER_DALVIK_OTHER_APP_CODE_CACHE = 24;
/** @hide */
public static final int OTHER_DALVIK_OTHER_COMPILER_METADATA = 25;
/** @hide */
public static final int OTHER_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE = 26;
/** @hide */
public static final int OTHER_DVK_STAT_DALVIK_OTHER_START =
OTHER_DALVIK_OTHER_LINEARALLOC - NUM_OTHER_STATS;
/** @hide */
public static final int OTHER_DVK_STAT_DALVIK_OTHER_END =
OTHER_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE - NUM_OTHER_STATS;
// Dex subsections (Boot vdex, App dex, and App vdex).
/** @hide */
public static final int OTHER_DEX_BOOT_VDEX = 27;
/** @hide */
public static final int OTHER_DEX_APP_DEX = 28;
/** @hide */
public static final int OTHER_DEX_APP_VDEX = 29;
/** @hide */
public static final int OTHER_DVK_STAT_DEX_START = OTHER_DEX_BOOT_VDEX - NUM_OTHER_STATS;
/** @hide */
public static final int OTHER_DVK_STAT_DEX_END = OTHER_DEX_APP_VDEX - NUM_OTHER_STATS;
// Art subsections (App image, boot image).
/** @hide */
public static final int OTHER_ART_APP = 30;
/** @hide */
public static final int OTHER_ART_BOOT = 31;
/** @hide */
public static final int OTHER_DVK_STAT_ART_START = OTHER_ART_APP - NUM_OTHER_STATS;
/** @hide */
public static final int OTHER_DVK_STAT_ART_END = OTHER_ART_BOOT - NUM_OTHER_STATS;
/** @hide */
@UnsupportedAppUsage
public static final int NUM_DVK_STATS = OTHER_ART_BOOT + 1 - OTHER_DALVIK_NORMAL;
/** @hide */
public static final int NUM_CATEGORIES = 9;
/** @hide */
public static final int OFFSET_PSS = 0;
/** @hide */
public static final int OFFSET_SWAPPABLE_PSS = 1;
/** @hide */
public static final int OFFSET_RSS = 2;
/** @hide */
public static final int OFFSET_PRIVATE_DIRTY = 3;
/** @hide */
public static final int OFFSET_SHARED_DIRTY = 4;
/** @hide */
public static final int OFFSET_PRIVATE_CLEAN = 5;
/** @hide */
public static final int OFFSET_SHARED_CLEAN = 6;
/** @hide */
public static final int OFFSET_SWAPPED_OUT = 7;
/** @hide */
public static final int OFFSET_SWAPPED_OUT_PSS = 8;
@UnsupportedAppUsage
private int[] otherStats = new int[(NUM_OTHER_STATS+NUM_DVK_STATS)*NUM_CATEGORIES];
public MemoryInfo() {
}
/**
* @hide Copy contents from another object.
*/
public void set(MemoryInfo other) {
dalvikPss = other.dalvikPss;
dalvikSwappablePss = other.dalvikSwappablePss;
dalvikRss = other.dalvikRss;
dalvikPrivateDirty = other.dalvikPrivateDirty;
dalvikSharedDirty = other.dalvikSharedDirty;
dalvikPrivateClean = other.dalvikPrivateClean;
dalvikSharedClean = other.dalvikSharedClean;
dalvikSwappedOut = other.dalvikSwappedOut;
dalvikSwappedOutPss = other.dalvikSwappedOutPss;
nativePss = other.nativePss;
nativeSwappablePss = other.nativeSwappablePss;
nativeRss = other.nativeRss;
nativePrivateDirty = other.nativePrivateDirty;
nativeSharedDirty = other.nativeSharedDirty;
nativePrivateClean = other.nativePrivateClean;
nativeSharedClean = other.nativeSharedClean;
nativeSwappedOut = other.nativeSwappedOut;
nativeSwappedOutPss = other.nativeSwappedOutPss;
otherPss = other.otherPss;
otherSwappablePss = other.otherSwappablePss;
otherRss = other.otherRss;
otherPrivateDirty = other.otherPrivateDirty;
otherSharedDirty = other.otherSharedDirty;
otherPrivateClean = other.otherPrivateClean;
otherSharedClean = other.otherSharedClean;
otherSwappedOut = other.otherSwappedOut;
otherSwappedOutPss = other.otherSwappedOutPss;
hasSwappedOutPss = other.hasSwappedOutPss;
System.arraycopy(other.otherStats, 0, otherStats, 0, otherStats.length);
}
/**
* Return total PSS memory usage in kB.
*/
public int getTotalPss() {
return dalvikPss + nativePss + otherPss + getTotalSwappedOutPss();
}
/**
* @hide Return total PSS memory usage in kB.
*/
@UnsupportedAppUsage
public int getTotalUss() {
return dalvikPrivateClean + dalvikPrivateDirty
+ nativePrivateClean + nativePrivateDirty
+ otherPrivateClean + otherPrivateDirty;
}
/**
* Return total PSS memory usage in kB mapping a file of one of the following extension:
* .so, .jar, .apk, .ttf, .dex, .odex, .oat, .art .
*/
public int getTotalSwappablePss() {
return dalvikSwappablePss + nativeSwappablePss + otherSwappablePss;
}
/**
* @hide Return total RSS memory usage in kB.
*/
public int getTotalRss() {
return dalvikRss + nativeRss + otherRss;
}
/**
* Return total private dirty memory usage in kB.
*/
public int getTotalPrivateDirty() {
return dalvikPrivateDirty + nativePrivateDirty + otherPrivateDirty;
}
/**
* Return total shared dirty memory usage in kB.
*/
public int getTotalSharedDirty() {
return dalvikSharedDirty + nativeSharedDirty + otherSharedDirty;
}
/**
* Return total shared clean memory usage in kB.
*/
public int getTotalPrivateClean() {
return dalvikPrivateClean + nativePrivateClean + otherPrivateClean;
}
/**
* Return total shared clean memory usage in kB.
*/
public int getTotalSharedClean() {
return dalvikSharedClean + nativeSharedClean + otherSharedClean;
}
/**
* Return total swapped out memory in kB.
* @hide
*/
public int getTotalSwappedOut() {
return dalvikSwappedOut + nativeSwappedOut + otherSwappedOut;
}
/**
* Return total swapped out memory in kB, proportional.
* @hide
*/
public int getTotalSwappedOutPss() {
return dalvikSwappedOutPss + nativeSwappedOutPss + otherSwappedOutPss;
}
/** @hide */
@UnsupportedAppUsage
public int getOtherPss(int which) {
return otherStats[which * NUM_CATEGORIES + OFFSET_PSS];
}
/** @hide */
public int getOtherSwappablePss(int which) {
return otherStats[which * NUM_CATEGORIES + OFFSET_SWAPPABLE_PSS];
}
/** @hide */
public int getOtherRss(int which) {
return otherStats[which * NUM_CATEGORIES + OFFSET_RSS];
}
/** @hide */
@UnsupportedAppUsage
public int getOtherPrivateDirty(int which) {
return otherStats[which * NUM_CATEGORIES + OFFSET_PRIVATE_DIRTY];
}
/** @hide */
@UnsupportedAppUsage
public int getOtherSharedDirty(int which) {
return otherStats[which * NUM_CATEGORIES + OFFSET_SHARED_DIRTY];
}
/** @hide */
public int getOtherPrivateClean(int which) {
return otherStats[which * NUM_CATEGORIES + OFFSET_PRIVATE_CLEAN];
}
/** @hide */
@UnsupportedAppUsage
public int getOtherPrivate(int which) {
return getOtherPrivateClean(which) + getOtherPrivateDirty(which);
}
/** @hide */
public int getOtherSharedClean(int which) {
return otherStats[which * NUM_CATEGORIES + OFFSET_SHARED_CLEAN];
}
/** @hide */
public int getOtherSwappedOut(int which) {
return otherStats[which * NUM_CATEGORIES + OFFSET_SWAPPED_OUT];
}
/** @hide */
public int getOtherSwappedOutPss(int which) {
return otherStats[which * NUM_CATEGORIES + OFFSET_SWAPPED_OUT_PSS];
}
/** @hide */
@UnsupportedAppUsage
public static String getOtherLabel(int which) {
switch (which) {
case OTHER_DALVIK_OTHER: return "Dalvik Other";
case OTHER_STACK: return "Stack";
case OTHER_CURSOR: return "Cursor";
case OTHER_ASHMEM: return "Ashmem";
case OTHER_GL_DEV: return "Gfx dev";
case OTHER_UNKNOWN_DEV: return "Other dev";
case OTHER_SO: return ".so mmap";
case OTHER_JAR: return ".jar mmap";
case OTHER_APK: return ".apk mmap";
case OTHER_TTF: return ".ttf mmap";
case OTHER_DEX: return ".dex mmap";
case OTHER_OAT: return ".oat mmap";
case OTHER_ART: return ".art mmap";
case OTHER_UNKNOWN_MAP: return "Other mmap";
case OTHER_GRAPHICS: return "EGL mtrack";
case OTHER_GL: return "GL mtrack";
case OTHER_OTHER_MEMTRACK: return "Other mtrack";
case OTHER_DALVIK_NORMAL: return ".Heap";
case OTHER_DALVIK_LARGE: return ".LOS";
case OTHER_DALVIK_ZYGOTE: return ".Zygote";
case OTHER_DALVIK_NON_MOVING: return ".NonMoving";
case OTHER_DALVIK_OTHER_LINEARALLOC: return ".LinearAlloc";
case OTHER_DALVIK_OTHER_ACCOUNTING: return ".GC";
case OTHER_DALVIK_OTHER_ZYGOTE_CODE_CACHE: return ".ZygoteJIT";
case OTHER_DALVIK_OTHER_APP_CODE_CACHE: return ".AppJIT";
case OTHER_DALVIK_OTHER_COMPILER_METADATA: return ".CompilerMetadata";
case OTHER_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE: return ".IndirectRef";
case OTHER_DEX_BOOT_VDEX: return ".Boot vdex";
case OTHER_DEX_APP_DEX: return ".App dex";
case OTHER_DEX_APP_VDEX: return ".App vdex";
case OTHER_ART_APP: return ".App art";
case OTHER_ART_BOOT: return ".Boot art";
default: return "????";
}
}
/**
* Returns the value of a particular memory statistic or {@code null} if no
* such memory statistic exists.
*
* <p>The following table lists the memory statistics that are supported.
* Note that memory statistics may be added or removed in a future API level.</p>
*
* <table>
* <thead>
* <tr>
* <th>Memory statistic name</th>
* <th>Meaning</th>
* <th>Example</th>
* <th>Supported (API Levels)</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>summary.java-heap</td>
* <td>The private Java Heap usage in kB. This corresponds to the Java Heap field
* in the App Summary section output by dumpsys meminfo.</td>
* <td>{@code 1442}</td>
* <td>23</td>
* </tr>
* <tr>
* <td>summary.native-heap</td>
* <td>The private Native Heap usage in kB. This corresponds to the Native Heap
* field in the App Summary section output by dumpsys meminfo.</td>
* <td>{@code 1442}</td>
* <td>23</td>
* </tr>
* <tr>
* <td>summary.code</td>
* <td>The memory usage for static code and resources in kB. This corresponds to
* the Code field in the App Summary section output by dumpsys meminfo.</td>
* <td>{@code 1442}</td>
* <td>23</td>
* </tr>
* <tr>
* <td>summary.stack</td>
* <td>The stack usage in kB. This corresponds to the Stack field in the
* App Summary section output by dumpsys meminfo.</td>
* <td>{@code 1442}</td>
* <td>23</td>
* </tr>
* <tr>
* <td>summary.graphics</td>
* <td>The graphics usage in kB. This corresponds to the Graphics field in the
* App Summary section output by dumpsys meminfo.</td>
* <td>{@code 1442}</td>
* <td>23</td>
* </tr>
* <tr>
* <td>summary.private-other</td>
* <td>Other private memory usage in kB. This corresponds to the Private Other
* field output in the App Summary section by dumpsys meminfo.</td>
* <td>{@code 1442}</td>
* <td>23</td>
* </tr>
* <tr>
* <td>summary.system</td>
* <td>Shared and system memory usage in kB. This corresponds to the System
* field output in the App Summary section by dumpsys meminfo.</td>
* <td>{@code 1442}</td>
* <td>23</td>
* </tr>
* <tr>
* <td>summary.total-pss</td>
* <td>Total PPS memory usage in kB.</td>
* <td>{@code 1442}</td>
* <td>23</td>
* </tr>
* <tr>
* <td>summary.total-swap</td>
* <td>Total swap usage in kB.</td>
* <td>{@code 1442}</td>
* <td>23</td>
* </tr>
* </tbody>
* </table>
*/
public String getMemoryStat(String statName) {
switch(statName) {
case "summary.java-heap":
return Integer.toString(getSummaryJavaHeap());
case "summary.native-heap":
return Integer.toString(getSummaryNativeHeap());
case "summary.code":
return Integer.toString(getSummaryCode());
case "summary.stack":
return Integer.toString(getSummaryStack());
case "summary.graphics":
return Integer.toString(getSummaryGraphics());
case "summary.private-other":
return Integer.toString(getSummaryPrivateOther());
case "summary.system":
return Integer.toString(getSummarySystem());
case "summary.total-pss":
return Integer.toString(getSummaryTotalPss());
case "summary.total-swap":
return Integer.toString(getSummaryTotalSwap());
default:
return null;
}
}
/**
* Returns a map of the names/values of the memory statistics
* that {@link #getMemoryStat(String)} supports.
*
* @return a map of the names/values of the supported memory statistics.
*/
public Map<String, String> getMemoryStats() {
Map<String, String> stats = new HashMap<String, String>();
stats.put("summary.java-heap", Integer.toString(getSummaryJavaHeap()));
stats.put("summary.native-heap", Integer.toString(getSummaryNativeHeap()));
stats.put("summary.code", Integer.toString(getSummaryCode()));
stats.put("summary.stack", Integer.toString(getSummaryStack()));
stats.put("summary.graphics", Integer.toString(getSummaryGraphics()));
stats.put("summary.private-other", Integer.toString(getSummaryPrivateOther()));
stats.put("summary.system", Integer.toString(getSummarySystem()));
stats.put("summary.total-pss", Integer.toString(getSummaryTotalPss()));
stats.put("summary.total-swap", Integer.toString(getSummaryTotalSwap()));
return stats;
}
/**
* Pss of Java Heap bytes in KB due to the application.
* Notes:
* * OTHER_ART is the boot image. Anything private here is blamed on
* the application, not the system.
* * dalvikPrivateDirty includes private zygote, which means the
* application dirtied something allocated by the zygote. We blame
* the application for that memory, not the system.
* * Does not include OTHER_DALVIK_OTHER, which is considered VM
* Overhead and lumped into Private Other.
* * We don't include dalvikPrivateClean, because there should be no
* such thing as private clean for the Java Heap.
* @hide
*/
@UnsupportedAppUsage
public int getSummaryJavaHeap() {
return dalvikPrivateDirty + getOtherPrivate(OTHER_ART);
}
/**
* Pss of Native Heap bytes in KB due to the application.
* Notes:
* * Includes private dirty malloc space.
* * We don't include nativePrivateClean, because there should be no
* such thing as private clean for the Native Heap.
* @hide
*/
@UnsupportedAppUsage
public int getSummaryNativeHeap() {
return nativePrivateDirty;
}
/**
* Pss of code and other static resource bytes in KB due to
* the application.
* @hide
*/
@UnsupportedAppUsage
public int getSummaryCode() {
return getOtherPrivate(OTHER_SO)
+ getOtherPrivate(OTHER_JAR)
+ getOtherPrivate(OTHER_APK)
+ getOtherPrivate(OTHER_TTF)
+ getOtherPrivate(OTHER_DEX)
+ getOtherPrivate(OTHER_OAT)
+ getOtherPrivate(OTHER_DALVIK_OTHER_ZYGOTE_CODE_CACHE)
+ getOtherPrivate(OTHER_DALVIK_OTHER_APP_CODE_CACHE);
}
/**
* Pss in KB of the stack due to the application.
* Notes:
* * Includes private dirty stack, which includes both Java and Native
* stack.
* * Does not include private clean stack, because there should be no
* such thing as private clean for the stack.
* @hide
*/
@UnsupportedAppUsage
public int getSummaryStack() {
return getOtherPrivateDirty(OTHER_STACK);
}
/**
* Pss in KB of graphics due to the application.
* Notes:
* * Includes private Gfx, EGL, and GL.
* * Warning: These numbers can be misreported by the graphics drivers.
* * We don't include shared graphics. It may make sense to, because
* shared graphics are likely buffers due to the application
* anyway, but it's simpler to implement to just group all shared
* memory into the System category.
* @hide
*/
@UnsupportedAppUsage
public int getSummaryGraphics() {
return getOtherPrivate(OTHER_GL_DEV)
+ getOtherPrivate(OTHER_GRAPHICS)
+ getOtherPrivate(OTHER_GL);
}
/**
* Pss in KB due to the application that haven't otherwise been
* accounted for.
* @hide
*/
@UnsupportedAppUsage
public int getSummaryPrivateOther() {
return getTotalPrivateClean()
+ getTotalPrivateDirty()
- getSummaryJavaHeap()
- getSummaryNativeHeap()
- getSummaryCode()
- getSummaryStack()
- getSummaryGraphics();
}
/**
* Pss in KB due to the system.
* Notes:
* * Includes all shared memory.
* @hide
*/
@UnsupportedAppUsage
public int getSummarySystem() {
return getTotalPss()
- getTotalPrivateClean()
- getTotalPrivateDirty();
}
/**
* Rss of Java Heap bytes in KB due to the application.
* @hide
*/
public int getSummaryJavaHeapRss() {
return dalvikRss + getOtherRss(OTHER_ART);
}
/**
* Rss of Native Heap bytes in KB due to the application.
* @hide
*/
public int getSummaryNativeHeapRss() {
return nativeRss;
}
/**
* Rss of code and other static resource bytes in KB due to
* the application.
* @hide
*/
public int getSummaryCodeRss() {
return getOtherRss(OTHER_SO)
+ getOtherRss(OTHER_JAR)
+ getOtherRss(OTHER_APK)
+ getOtherRss(OTHER_TTF)
+ getOtherRss(OTHER_DEX)
+ getOtherRss(OTHER_OAT)
+ getOtherRss(OTHER_DALVIK_OTHER_ZYGOTE_CODE_CACHE)
+ getOtherRss(OTHER_DALVIK_OTHER_APP_CODE_CACHE);
}
/**
* Rss in KB of the stack due to the application.
* @hide
*/
public int getSummaryStackRss() {
return getOtherRss(OTHER_STACK);
}
/**
* Rss in KB of graphics due to the application.
* @hide
*/
public int getSummaryGraphicsRss() {
return getOtherRss(OTHER_GL_DEV)
+ getOtherRss(OTHER_GRAPHICS)
+ getOtherRss(OTHER_GL);
}
/**
* Rss in KB due to either the application or system that haven't otherwise been
* accounted for.
* @hide
*/
public int getSummaryUnknownRss() {
return getTotalRss()
- getSummaryJavaHeapRss()
- getSummaryNativeHeapRss()
- getSummaryCodeRss()
- getSummaryStackRss()
- getSummaryGraphicsRss();
}
/**
* Total Pss in KB.
* @hide
*/
public int getSummaryTotalPss() {
return getTotalPss();
}
/**
* Total Swap in KB.
* Notes:
* * Some of this memory belongs in other categories, but we don't
* know if the Swap memory is shared or private, so we don't know
* what to blame on the application and what on the system.
* For now, just lump all the Swap in one place.
* For kernels reporting SwapPss {@link #getSummaryTotalSwapPss()}
* will report the application proportional Swap.
* @hide
*/
public int getSummaryTotalSwap() {
return getTotalSwappedOut();
}
/**
* Total proportional Swap in KB.
* Notes:
* * Always 0 if {@link #hasSwappedOutPss} is false.
* @hide
*/
public int getSummaryTotalSwapPss() {
return getTotalSwappedOutPss();
}
/**
* Return true if the kernel is reporting pss swapped out... that is, if
* {@link #getSummaryTotalSwapPss()} will return non-0 values.
* @hide
*/
public boolean hasSwappedOutPss() {
return hasSwappedOutPss;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
...
}
public void readFromParcel(Parcel source) {
...
}
public static final @android.annotation.NonNull Creator<MemoryInfo> CREATOR = new Creator<MemoryInfo>() {
...
};
private MemoryInfo(Parcel source) {
...
}
}
网友评论