单个进程的内存占用统计
打印单个进程的内存占用详情命令是:
adb shell "dumpsys meminfo -a pid"
流程图展示
源码分析
dumpsys meminfo是dumpsys进程通过binder调用到system_server进程中完成的。对于进程的内存占用数据获取以及计算都是在system_server进程中完成的。我们先看下程序的入口。
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) {
// dumpsys meminfo的调用入口
mActivityManagerService.dumpApplicationMemoryUsage(
fd, pw, " ", args, false, null, asProto);
}
};
MemBinder(ActivityManagerService activityManagerService) {
mActivityManagerService = activityManagerService;
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
"meminfo", pw)) return;
PriorityDump.dump(mPriorityDumper, fd, pw, args);
}
}
final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, String prefix,
String[] args, boolean brief, PrintWriter categoryPw, boolean asProto) {
// 创建MemoryUsageDumpOptions类来保存从args中解析出的命令参数
MemoryUsageDumpOptions opts = new MemoryUsageDumpOptions();
opts.dumpDetails = false;
opts.dumpFullDetails = false;
opts.dumpDalvik = false;
opts.dumpSummaryOnly = false;
opts.dumpUnreachable = false;
opts.oomOnly = false;
opts.isCompact = false;
opts.localOnly = false;
opts.packages = false;
opts.isCheckinRequest = false;
opts.dumpSwapPss = false;
opts.dumpProto = asProto;
int opti = 0;
while (opti < args.length) {
String opt = args[opti];
if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') {
break;
}
opti++;
// 打印单个进程的内存占用详细参数是-a
if ("-a".equals(opt)) {
opts.dumpDetails = true;
opts.dumpFullDetails = true;
opts.dumpDalvik = true;
opts.dumpSwapPss = true;
}
}
dumpApplicationMemoryUsage(fd, pw, prefix, opts, innerArgs, brief, procs, categoryPw);
}
private final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, String prefix,
MemoryUsageDumpOptions opts, String[] innerArgs, boolean brief,
ArrayList<ProcessRecord> procs, PrintWriter categoryPw) {
// 打印meminfo头部信息
dumpApplicationMemoryUsageHeader(pw, uptime, realtime, opts.isCheckinRequest, opts.isCompact);
// 调用JNI接口,获取指定pid的内存占用信息
Debug.getMemoryInfo(pid, mi);
try {
TransferPipe tp = new TransferPipe();
try {
// 通过binder调用到ApplicationThread,并打印出之前获取的MemoryInfo信息和进程内部其他部分内存占用情况
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();
}
}
}
frameworks/base/core/java/android/app/ActivityThread.java
private void dumpMemInfo(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin,
boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly, boolean dumpUnreachable) {
// 打印MemoryInfo中的内存占用情况
dumpMemInfoTable(pw, memInfo, checkin, dumpFullInfo, dumpDalvik, dumpSummaryOnly,
Process.myPid(),
(mBoundApplication != null) ? mBoundApplication.processName : "unknown",
nativeMax, nativeAllocated, nativeFree,
dalvikMax, dalvikAllocated, dalvikFree);
// 打印Objects
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);
// 打印SQL情况
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(" ");
// 打印DATABASES
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);
}
}
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) {
// 打印Native Heap行
printRow(pw, HEAP_FULL_COLUMN, "Native Heap", memInfo.nativePss,
memInfo.nativeSwappablePss, memInfo.nativeSharedDirty,
memInfo.nativePrivateDirty, memInfo.nativeSharedClean,
memInfo.nativePrivateClean, memInfo.hasSwappedOutPss ?
memInfo.nativeSwappedOutPss : memInfo.nativeSwappedOut,
nativeMax, nativeAllocated, nativeFree);
// 打印Dalvik Heap行
printRow(pw, HEAP_FULL_COLUMN, "Dalvik Heap", memInfo.dalvikPss,
memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty,
memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean,
memInfo.dalvikPrivateClean, memInfo.hasSwappedOutPss ?
memInfo.dalvikSwappedOutPss : memInfo.dalvikSwappedOut,
dalvikMax, dalvikAllocated, dalvikFree);
// 循环遍历打印所有的other类别的内存占用情况
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);
if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
|| mySharedClean != 0 || myPrivateClean != 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,
"", "", "");
} else {
printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
myPss, myPrivateDirty,
myPrivateClean,
memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut,
"", "", "");
}
otherPss -= myPss;
otherSwappablePss -= mySwappablePss;
otherSharedDirty -= mySharedDirty;
otherPrivateDirty -= myPrivateDirty;
otherSharedClean -= mySharedClean;
otherPrivateClean -= myPrivateClean;
otherSwappedOut -= mySwappedOut;
otherSwappedOutPss -= mySwappedOutPss;
}
}
// 打印Unknown行内存占用情况
printRow(pw, HEAP_FULL_COLUMN, "Unknown", otherPss, otherSwappablePss,
otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean,
memInfo.hasSwappedOutPss ? otherSwappedOutPss : otherSwappedOut,
"", "", "");
// 打印TOTAL行内存的占用情况
printRow(pw, HEAP_FULL_COLUMN, "TOTAL", memInfo.getTotalPss(),
memInfo.getTotalSwappablePss(),
memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(),
memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(),
memInfo.hasSwappedOutPss ? memInfo.getTotalSwappedOutPss() :
memInfo.getTotalSwappedOut(),
nativeMax+dalvikMax, nativeAllocated+dalvikAllocated,
nativeFree+dalvikFree);
// 打印Dalvik Details
if (dumpDalvik) {
pw.println(" ");
pw.println(" Dalvik Details");
for (int i=Debug.MemoryInfo.NUM_OTHER_STATS;
i<Debug.MemoryInfo.NUM_OTHER_STATS + Debug.MemoryInfo.NUM_DVK_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);
if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
|| mySharedClean != 0 || myPrivateClean != 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,
"", "", "");
} else {
printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
myPss, myPrivateDirty,
myPrivateClean,
memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut,
"", "", "");
}
}
}
}
// 打印App Summary
pw.println(" ");
pw.println(" App Summary");
printRow(pw, ONE_COUNT_COLUMN_HEADER, "", "Pss(KB)");
printRow(pw, ONE_COUNT_COLUMN_HEADER, "", "------");
printRow(pw, ONE_COUNT_COLUMN,
"Java Heap:", memInfo.getSummaryJavaHeap());
printRow(pw, ONE_COUNT_COLUMN,
"Native Heap:", memInfo.getSummaryNativeHeap());
printRow(pw, ONE_COUNT_COLUMN,
"Code:", memInfo.getSummaryCode());
printRow(pw, ONE_COUNT_COLUMN,
"Stack:", memInfo.getSummaryStack());
printRow(pw, ONE_COUNT_COLUMN,
"Graphics:", memInfo.getSummaryGraphics());
printRow(pw, ONE_COUNT_COLUMN,
"Private Other:", memInfo.getSummaryPrivateOther());
printRow(pw, ONE_COUNT_COLUMN,
"System:", memInfo.getSummarySystem());
pw.println(" ");
}
frameworks/base/core/java/android/os/Debug.java
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. */
public int dalvikSwappablePss;
/** @hide The resident set size for dalvik heap. (Without other Dalvik overhead.) */
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. */
public int dalvikPrivateClean;
/** The shared clean pages used by dalvik heap. */
/** @hide We may want to expose this, eventually. */
public int dalvikSharedClean;
/** The dirty dalvik pages that have been swapped out. */
/** @hide We may want to expose this, eventually. */
public int dalvikSwappedOut;
/** The dirty dalvik pages that have been swapped out, proportional. */
/** @hide We may want to expose this, eventually. */
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. */
public int nativeSwappablePss;
/** @hide The resident set size for the native heap. */
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. */
public int nativePrivateClean;
/** The shared clean pages used by the native heap. */
/** @hide We may want to expose this, eventually. */
public int nativeSharedClean;
/** The dirty native pages that have been swapped out. */
/** @hide We may want to expose this, eventually. */
public int nativeSwappedOut;
/** The dirty native pages that have been swapped out, proportional. */
/** @hide We may want to expose this, eventually. */
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. */
public int otherSwappablePss;
/** @hide The resident set size for everything else. */
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. */
public int otherPrivateClean;
/** The shared clean pages used by everything else. */
/** @hide We may want to expose this, eventually. */
public int otherSharedClean;
/** The dirty pages used by anyting else that have been swapped out. */
/** @hide We may want to expose this, eventually. */
public int otherSwappedOut;
/** The dirty pages used by anyting else that have been swapped out, proportional. */
/** @hide We may want to expose this, eventually. */
public int otherSwappedOutPss;
/** Whether the kernel reports proportional swap usage */
/** @hide */
public boolean hasSwappedOutPss;
private int[] otherStats = new int[(NUM_OTHER_STATS+NUM_DVK_STATS)*NUM_CATEGORIES];
/**
* Note: currently only works when the requested pid has the same UID
* as the caller.
* @hide
*/
public static native void getMemoryInfo(int pid, MemoryInfo memoryInfo);
}
frameworks/base/core/jni/android_os_Debug.cpp
// Container used to retrieve graphics memory pss
struct graphics_memory_pss
{
int graphics;
int gl;
int other;
};
/*
* Uses libmemtrack to retrieve graphics memory that the process is using.
* Any graphics memory reported in /proc/pid/smaps is not included here.
*/
static int read_memtrack_memory(struct memtrack_proc* p, int pid,
struct graphics_memory_pss* graphics_mem)
{
int err = memtrack_proc_get(p, pid);
if (err != 0) {
ALOGW("failed to get memory consumption info: %d", err);
return err;
}
// 从memtrack中获取graphics pss
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;
// 从memtrack中获取gl pss
pss = memtrack_proc_gl_pss(p);
if (pss < 0) {
ALOGW("failed to get gl pss: %zd", pss);
return pss;
}
graphics_mem->gl = pss / 1024;
// 从memtrack中获取other pss
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;
}
static void load_maps(int pid, stats_t* stats, bool* foundSwapPss)
{
*foundSwapPss = false;
std::string smaps_path = base::StringPrintf("/proc/%d/smaps", pid);
UniqueFile fp = MakeUniqueFile(smaps_path.c_str(), "re");
if (fp == nullptr) return;
// 解析/proc/pid/smaps信息
read_mapinfo(fp.get(), stats, foundSwapPss);
}
static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
jint pid, jobject object)
{
bool foundSwapPss;
stats_t stats[_NUM_HEAP];
memset(&stats, 0, sizeof(stats));
// 该方法的主要业务逻辑是遍历/proc/pid/smaps,根据内存的不同分类,计算出各个类别的内存占用情况
load_maps(pid, stats, &foundSwapPss);
struct graphics_memory_pss graphics_mem;
// 获取没有归属到/proc/pid/smaps中的graphics,gl,other类别的内存。
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;
}
// 将包括graphics,gl,other在内的内存统一归并到HEAP_UNKNOWN
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;
}
// 将HEAP_UNKNOWN, HEAP_DALVIK, HEAP_NATIVE传递给Java层的MemoryInfo对应的实例
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;
}
int j=0;
// 将HEAP_DALVIK_OTHER到HEAP_OTHER_MEMTRACK的内存数据传递给Java层的otherStats数组中
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);
}
system/core/libmemtrack/memtrack.cpp
static int memtrack_proc_get_type(memtrack_proc_type *t,
pid_t pid, MemtrackType type)
{
int err = 0;
android::sp<IMemtrack> memtrack = get_instance();
if (memtrack == nullptr)
return -1;
Return<void> ret = memtrack->getMemory(pid, type,
[&t, &err](MemtrackStatus status, hidl_vec<MemtrackRecord> records) {
if (status != MemtrackStatus::SUCCESS) {
err = -1;
t->records.resize(0);
}
t->records.resize(records.size());
for (size_t i = 0; i < records.size(); i++) {
t->records[i].sizeInBytes = records[i].sizeInBytes;
t->records[i].flags = records[i].flags;
}
});
return ret.isOk() ? err : -1;
}
int memtrack_proc_get(memtrack_proc *p, pid_t pid)
{
if (!p) {
return -EINVAL;
}
p->pid = pid;
for (uint32_t i = 0; i < (uint32_t)MemtrackType::NUM_TYPES; i++) {
int ret = memtrack_proc_get_type(&p->types[i], pid, (MemtrackType)i);
if (ret != 0)
return ret;
}
return memtrack_proc_sanity_check(p);
}
ssize_t memtrack_proc_graphics_pss(memtrack_proc *p)
{
std::vector<MemtrackType> types = { MemtrackType::GRAPHICS };
return memtrack_proc_sum(p, types,
(uint32_t)MemtrackFlag::SMAPS_UNACCOUNTED);
}
ssize_t memtrack_proc_gl_pss(memtrack_proc *p)
{
std::vector<MemtrackType> types = { MemtrackType::GL };
return memtrack_proc_sum(p, types,
(uint32_t)MemtrackFlag::SMAPS_UNACCOUNTED);
}
ssize_t memtrack_proc_other_pss(memtrack_proc *p)
{
std::vector<MemtrackType> types = { MemtrackType::MULTIMEDIA,
MemtrackType::CAMERA, MemtrackType::OTHER };
return memtrack_proc_sum(p, types,
(uint32_t)MemtrackFlag::SMAPS_UNACCOUNTED);
}
hardware/interfaces/memtrack/1.0/default/Memtrack.cpp
Return<void> Memtrack::getMemory(int32_t pid, MemtrackType type,
getMemory_cb _hidl_cb) {
hidl_vec<MemtrackRecord> records;
size_t temp = 0;
size_t *size = &temp;
int ret = 0;
if (mModule->getMemory == nullptr)
{
_hidl_cb(MemtrackStatus::SUCCESS, records);
return Void();
}
ret = mModule->getMemory(mModule, pid, static_cast<memtrack_type>(type),
NULL, size);
if (ret == 0)
{
memtrack_record *legacy_records = new memtrack_record[*size];
ret = mModule->getMemory(mModule, pid,
static_cast<memtrack_type>(type), legacy_records, size);
if (ret == 0)
{
records.resize(*size);
for(size_t i = 0; i < *size; i++)
{
records[i].sizeInBytes = legacy_records[i].size_in_bytes;
records[i].flags = legacy_records[i].flags;
}
}
delete[] legacy_records;
}
_hidl_cb(MemtrackStatus::SUCCESS, records);
return Void();
}
hardware/qcom/display/libmemtrack/memtrack_msm.c
int msm_memtrack_get_memory(const struct memtrack_module *module,
pid_t pid,
int type,
struct memtrack_record *records,
size_t *num_records)
{
if(!module)
return -1;
if (type == MEMTRACK_TYPE_GL || type == MEMTRACK_TYPE_GRAPHICS) {
return kgsl_memtrack_get_memory(pid, type, records, num_records);
}
return -EINVAL;
}
hardware/qcom/display/libmemtrack/kgsl.c
int kgsl_memtrack_get_memory(pid_t pid, enum memtrack_type type,
struct memtrack_record *records,
size_t *num_records)
{
size_t allocated_records = min(*num_records, ARRAY_SIZE(record_templates));
char syspath[128];
size_t accounted_size = 0;
size_t unaccounted_size = 0;
FILE *fp;
int ret;
*num_records = ARRAY_SIZE(record_templates);
/* fastpath to return the necessary number of records */
if (allocated_records == 0)
return 0;
memcpy(records, record_templates,
sizeof(struct memtrack_record) * allocated_records);
if (type == MEMTRACK_TYPE_GL) {
snprintf(syspath, sizeof(syspath),
"/sys/class/kgsl/kgsl/proc/%d/gpumem_mapped", pid);
fp = fopen(syspath, "r");
if (fp == NULL)
return -errno;
ret = fscanf(fp, "%zu", &accounted_size);
if (ret != 1) {
fclose(fp);
return -EINVAL;
}
fclose(fp);
snprintf(syspath, sizeof(syspath),
"/sys/class/kgsl/kgsl/proc/%d/gpumem_unmapped", pid);
fp = fopen(syspath, "r");
if (fp == NULL) {
return -errno;
}
ret = fscanf(fp, "%zu", &unaccounted_size);
if (ret != 1) {
fclose(fp);
return -EINVAL;
}
fclose(fp);
} else if (type == MEMTRACK_TYPE_GRAPHICS) {
snprintf(syspath, sizeof(syspath),
"/sys/class/kgsl/kgsl/proc/%d/imported_mem", pid);
fp = fopen(syspath, "r");
if (fp == NULL)
return -errno;
ret = fscanf(fp, "%zu", &unaccounted_size);
if (ret != 1) {
fclose(fp);
return -EINVAL;
}
fclose(fp);
}
if (allocated_records > 0)
records[0].size_in_bytes = accounted_size;
if (allocated_records > 1)
records[1].size_in_bytes = unaccounted_size;
return 0;
}
dumpsys meminfo流程
adb shell "dumpsys meminfo"
该命令可以打印出系统中全部进程的内存占用情况。前面部分和打印单个进程内存情况完全一致。下面重点看下差异部分。
源码分析
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
static final int[] DUMP_MEM_OOM_ADJ = new int[] {
ProcessList.NATIVE_ADJ,
ProcessList.SYSTEM_ADJ, ProcessList.PERSISTENT_PROC_ADJ,
ProcessList.PERSISTENT_SERVICE_ADJ, ProcessList.FOREGROUND_APP_ADJ,
ProcessList.VISIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_APP_ADJ,
ProcessList.BACKUP_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ,
ProcessList.SERVICE_ADJ, ProcessList.HOME_APP_ADJ,
ProcessList.PREVIOUS_APP_ADJ, ProcessList.SERVICE_B_ADJ, ProcessList.CACHED_APP_MIN_ADJ
};
static final String[] DUMP_MEM_OOM_LABEL = new String[] {
"Native",
"System", "Persistent", "Persistent Service", "Foreground",
"Visible", "Perceptible",
"Heavy Weight", "Backup",
"A Services", "Home",
"Previous", "B Services", "Cached"
};
static final String[] DUMP_MEM_OOM_COMPACT_LABEL = new String[] {
"native",
"sys", "pers", "persvc", "fore",
"vis", "percept",
"heavy", "backup",
"servicea", "home",
"prev", "serviceb", "cached"
};
final static class MemItem {
final boolean isProc;
final String label;
final String shortLabel;
final long pss;
final long swapPss;
final int id;
final boolean hasActivities;
ArrayList<MemItem> subitems;
public MemItem(String _label, String _shortLabel, long _pss, long _swapPss, int _id,
boolean _hasActivities) {
isProc = true;
label = _label;
shortLabel = _shortLabel;
pss = _pss;
swapPss = _swapPss;
id = _id;
hasActivities = _hasActivities;
}
public MemItem(String _label, String _shortLabel, long _pss, long _swapPss, int _id) {
isProc = false;
label = _label;
shortLabel = _shortLabel;
pss = _pss;
swapPss = _swapPss;
id = _id;
hasActivities = false;
}
}
private final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, String prefix,
MemoryUsageDumpOptions opts, String[] innerArgs, boolean brief,
ArrayList<ProcessRecord> procs, PrintWriter categoryPw) {
// 遍历获取到的pids
for (int i = procs.size() - 1 ; i >= 0 ; i--) {
// 获取对应pid的pss内存占用情况
mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null);
// 给每一个pid创建一个MemItem,并放入Map中
MemItem pssItem = new MemItem(r.processName + " (pid " + pid +
(hasActivities ? " / activities)" : ")"), r.processName, myTotalPss,
myTotalSwapPss, pid, hasActivities);
procMems.add(pssItem);
procMemsMap.put(pid, pssItem);
// 统计other类别的内存总体暂用情况
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;
}
// 按照oomadj的分类,将内存进行累加并同时把进程按照oomadj归类。
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);
break;
}
}
}
// 将内存分类打印
if (!brief && !opts.oomOnly && !opts.isCompact) {
pw.println();
pw.println("Total PSS by process:");
dumpMemItems(pw, " ", "proc", procMems, true, opts.isCompact, opts.dumpSwapPss);
pw.println();
}
if (!opts.isCompact) {
pw.println("Total PSS by OOM adjustment:");
}
dumpMemItems(pw, " ", "oom", oomMems, false, opts.isCompact, 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, opts.dumpSwapPss);
}
// 从/proc/meminfo中获取内存数据
MemInfoReader memInfo = new MemInfoReader();
memInfo.readMemInfo();
// 打印Total RAM
pw.print("Total RAM: "); pw.print(stringifyKBSize(memInfo.getTotalSizeKb()));
// 打印Free RAM
pw.print(" Free RAM: ");
pw.print(stringifyKBSize(cachedPss + memInfo.getCachedSizeKb()
+ memInfo.getFreeSizeKb()));
// 计算LostRAM
long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
- memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
- memInfo.getKernelUsedSizeKb() - memInfo.getZramTotalSizeKb();
// 打印Used RAM
pw.print(" Used RAM: "); pw.print(stringifyKBSize(totalPss - cachedPss
+ memInfo.getKernelUsedSizeKb())); pw.print(" (");
pw.print(stringifyKBSize(totalPss - cachedPss)); pw.print(" used pss + ");
pw.print(stringifyKBSize(memInfo.getKernelUsedSizeKb())); pw.print(" kernel)\n");
// 打印Lost RAM
pw.print(" Lost RAM: "); pw.println(stringifyKBSize(lostRAM));
// 打印Tuning
pw.print(" Tuning: ");
// 打印单个Java进程的内存大小限制
pw.print(ActivityManager.staticGetMemoryClass());
// 打印单个large进程内存大小限制
pw.print(" (large ");
pw.print(ActivityManager.staticGetLargeMemoryClass());
// 打印发生LMK的内存大小
pw.print("), oom ");
pw.print(stringifySize(
mProcessList.getMemLevel(ProcessList.CACHED_APP_MAX_ADJ), 1024));
// 打印后台应用最多的cache内存大小
pw.print(", restore limit ");
pw.print(stringifyKBSize(mProcessList.getCachedRestoreThresholdKb()));
if (ActivityManager.isLowRamDeviceStatic()) {
pw.print(" (low-ram)");
}
// 打印硬件加速状态
if (ActivityManager.isHighEndGfx()) {
pw.print(" (high-end-gfx)");
}
}
frameworks/base/core/jni/android_os_Debug.cpp
static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid,
jlongArray outUssSwapPssRss, jlongArray outMemtrack)
{
struct graphics_memory_pss graphics_mem;
// 获取graphics gl other内存总和计入pss中
if (read_memtrack_memory(pid, &graphics_mem) == 0) {
pss = uss = memtrack = graphics_mem.graphics + graphics_mem.gl + graphics_mem.other;
}
// 获取句柄,/proc/pid/smaps_rollup或者/proc/pid/smaps,然后再计算出进程的pss占用
UniqueFile fp = OpenSmapsOrRollup(pid);
}
UniqueFile OpenSmapsOrRollup(int pid)
{
enum pss_rollup_support rollup_support =
g_pss_rollup_support.load(std::memory_order_relaxed);
if (rollup_support != PSS_ROLLUP_UNSUPPORTED) {
std::string smaps_rollup_path =
base::StringPrintf("/proc/%d/smaps_rollup", pid);
UniqueFile fp_rollup = MakeUniqueFile(smaps_rollup_path.c_str(), "re");
if (fp_rollup == nullptr && errno != ENOENT) {
return fp_rollup; // Actual error, not just old kernel.
}
if (fp_rollup != nullptr) {
if (rollup_support == PSS_ROLLUP_UNTRIED) {
ALOGI("using rollup pss collection");
g_pss_rollup_support.store(PSS_ROLLUP_SUPPORTED,
std::memory_order_relaxed);
}
return fp_rollup;
}
g_pss_rollup_support.store(PSS_ROLLUP_UNSUPPORTED,
std::memory_order_relaxed);
}
std::string smaps_path = base::StringPrintf("/proc/%d/smaps", pid);
return MakeUniqueFile(smaps_path.c_str(), "re");
}
以上就是dumpsys meminfo的整个代码流程。总得来说,就是以下几个步骤:
1.获取进程列表
2.循环遍历进程列表从/proc/pid/smaps_rollup中统计出每个进程的pss占用情况
3.将进程的内存数据再按照几个分类,分别统计
4.按照之前的分类统计把内存占用情况打印出来