简单的查看内存信息可以使用命令:adb shell dumpsys meminfo
其入口:
android/frameworks/native/cmds/dumpsys/main.cpp
int main(int argc, char* const argv[]) {
signal(SIGPIPE, SIG_IGN);
sp<IServiceManager> sm = defaultServiceManager();
fflush(stdout);
if (sm == nullptr) {
ALOGE("Unable to get default service manager!");
aerr << "dumpsys: Unable to get default service manager!" << endl;
return 20;
}
Dumpsys dumpsys(sm.get());
return dumpsys.main(argc, argv);
}
通过解析参数,会调用对应servicemanager中注册的服务meminfo 的dump接口
android/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public void setSystemProcess() {
ServiceManager.addService("meminfo", new MemBinder(this));
}
所以执行adb shell dumpsys meminfo 后最终会调用ActivityManagerService如下dump接口:
static class MemBinder extends Binder {
ActivityManagerService mActivityManagerService;
MemBinder(ActivityManagerService activityManagerService) {
mActivityManagerService = activityManagerService;
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
"meminfo", pw)) return;
mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, false, null);
}
}
dump出的信息类似以下(有精简)
Applications Memory Usage (in Kilobytes):
Uptime: 14442 Realtime: 14442
Total PSS by process:
87,694K: system (pid 1648)
62,996K: com.android.systemui (pid 1786)
58,326K: com.google.android.apps.nexuslauncher (pid 2297 / activities)
47,529K: com.google.android.googlequicksearchbox:search (pid 2367)
46,386K: com.google.android.apps.maps (pid 2655)
38,446K: com.google.android.inputmethod.latin (pid 1771)
36,308K: com.google.android.gms.persistent (pid 2222)
22,993K: com.android.phone (pid 1897)
22,085K: zygote (pid 1521)
21,734K: com.google.android.gms (pid 2594)
21,199K: com.google.android.apps.photos (pid 2878)
15,911K: com.google.android.apps.messaging:rcs (pid 2849)
14,825K: com.google.android.apps.messaging (pid 2786)
11,901K: com.google.android.dialer (pid 2134)
11,842K: com.android.settings (pid 1928)
11,403K: android.process.acore (pid 2449)
11,009K: webview_zygote32 (pid 1818)
......
Total PSS by OOM adjustment:
181,467K: Native
22,085K: zygote (pid 1521)
21,199K: com.google.android.apps.photos (pid 2878)
15,911K: com.google.android.apps.messaging:rcs (pid 2849)
11,009K: webview_zygote32 (pid 1818)
7,350K: media.codec (pid 1534)
6,036K: zygote (pid 2927)
5,862K: audioserver (pid 1522)
5,359K: rild (pid 1535)
5,236K: wpa_supplicant (pid 1893)
5,135K: mediaserver (pid 1530)
4,379K: surfaceflinger (pid 1439)
......
87,694K: System
87,694K: system (pid 1648)
85,989K: Persistent
62,996K: com.android.systemui (pid 1786)
22,993K: com.android.phone (pid 1897)
142,163K: Foreground
58,326K: com.google.android.apps.nexuslauncher (pid 2297 / activities)
47,529K: com.google.android.googlequicksearchbox:search (pid 2367)
36,308K: com.google.android.gms.persistent (pid 2222)
20,794K: Visible
10,085K: com.google.android.googlequicksearchbox:interactor (pid 2240)
6,223K: com.google.process.gservices (pid 2324)
4,486K: com.android.printspooler (pid 2398)
38,446K: Perceptible
38,446K: com.google.android.inputmethod.latin (pid 1771)
53,109K: Previous
21,734K: com.google.android.gms (pid 2594)
11,403K: android.process.acore (pid 2449)
9,451K: android.process.media (pid 2217)
5,795K: com.google.process.gapps (pid 2507)
4,726K: com.google.android.partnersetup (pid 2533)
118,227K: Cached
46,386K: com.google.android.apps.maps (pid 2655)
14,825K: com.google.android.apps.messaging (pid 2786)
11,901K: com.google.android.dialer (pid 2134)
11,842K: com.android.settings (pid 1928)
8,478K: com.google.android.deskclock (pid 2057)
6,047K: com.android.contacts (pid 2551)
5,523K: com.android.cellbroadcastreceiver (pid 2112)
5,187K: com.android.providers.calendar (pid 2616)
4,208K: com.android.managedprovisioning (pid 2579)
3,830K: com.android.keychain (pid 2427)
Total PSS by category:
204,398K: .dex mmap
109,618K: .so mmap
105,009K: Native
85,917K: .apk mmap
57,481K: Dalvik
39,615K: .oat mmap
32,382K: .art mmap
24,693K: Dalvik Other
21,536K: Unknown
19,962K: Other mmap
14,090K: .ttf mmap
11,148K: Stack
1,378K: Other dev
516K: Ashmem
140K: .jar mmap
6K: Cursor
0K: Gfx dev
0K: EGL mtrack
0K: GL mtrack
0K: Other mtrack
Total RAM: 1,530,604K (status normal)
Free RAM: 775,291K ( 118,227K cached pss + 139,608K cached kernel + 517,456K free)
Used RAM: 690,302K ( 609,662K used pss + 80,640K kernel)
Lost RAM: 65,011K
Tuning: 192 (large 192), oom 184,320K, restore limit 61,440K (high-end-gfx)
Uptime: 表示启动到现在的时长,不包含休眠的时间,单位毫秒(ms)
Realtime: 表示启动到现在的时长,包含休眠的时间,单位毫秒(ms)
PSS:Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存),比如2个进程使用10KB的共享库,那么每个进程算5KB内存占用到PSS中。
Total PSS by process
这个标签下面的就是按照以PPS方式统计的,进程使用内存按多到少排列出来。
Total PSS by OOM adjustment:
这个下面是按OOM adj值排列的,就是lowmem kill时的优先级,从高到低排列
依次对应如下adj值,具体含义见注释:
android/frameworks/base/services/core/java/com/android/server/am/ProcessList.java
// Adjustment used in certain places where we don't know it yet.
// (Generally this is something that is going to be cached, but we
// don't know the exact value in the cached range to assign yet.)
static final int UNKNOWN_ADJ = 1001;
// This is a process only hosting activities that are not visible,
// so it can be killed without any disruption.
static final int CACHED_APP_MAX_ADJ = 906;
static final int CACHED_APP_MIN_ADJ = 900;
// The B list of SERVICE_ADJ -- these are the old and decrepit
// services that aren't as shiny and interesting as the ones in the A list.
static final int SERVICE_B_ADJ = 800;
// This is the process of the previous application that the user was in.
// This process is kept above other things, because it is very common to
// switch back to the previous app. This is important both for recent
// task switch (toggling between the two top recent apps) as well as normal
// UI flow such as clicking on a URI in the e-mail app to view in the browser,
// and then pressing back to return to e-mail.
static final int PREVIOUS_APP_ADJ = 700;
// This is a process holding the home application -- we want to try
// avoiding killing it, even if it would normally be in the background,
// because the user interacts with it so much.
static final int HOME_APP_ADJ = 600;
// This is a process holding an application service -- killing it will not
// have much of an impact as far as the user is concerned.
static final int SERVICE_ADJ = 500;
// This is a process with a heavy-weight application. It is in the
// background, but we want to try to avoid killing it. Value set in
// system/rootdir/init.rc on startup.
static final int HEAVY_WEIGHT_APP_ADJ = 400;
// This is a process currently hosting a backup operation. Killing it
// is not entirely fatal but is generally a bad idea.
static final int BACKUP_APP_ADJ = 300;
// This is a process only hosting components that are perceptible to the
// user, and we really want to avoid killing them, but they are not
// immediately visible. An example is background music playback.
static final int PERCEPTIBLE_APP_ADJ = 200;
// This is a process only hosting activities that are visible to the
// user, so we'd prefer they don't disappear.
static final int VISIBLE_APP_ADJ = 100;
// This is the process running the current foreground app. We'd really
// rather not kill it!
static final int FOREGROUND_APP_ADJ = 0;
// This is a process that the system or a persistent process has bound to,
// and indicated it is important.
static final int PERSISTENT_SERVICE_ADJ = -700;
// This is a system persistent process, such as telephony. Definitely
// don't want to kill it, but doing so is not completely fatal.
static final int PERSISTENT_PROC_ADJ = -800;
// The system process runs at the default adjustment.
static final int SYSTEM_ADJ = -900;
// Special code for native processes that are not being managed by the system (so
// don't have an oom adj assigned by the system).
static final int NATIVE_ADJ = -1000;
android/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"
};
其中Cached标签下的进程是随时可以回收内存的缓存进程,所以该部分内存在后面会统计到Free RAM字段中
Total RAM: 1,530,604K (status normal)
Free RAM: 775,291K ( 118,227K cached pss + 139,608K cached kernel + 517,456K free)
Used RAM: 690,302K ( 609,662K used pss + 80,640K kernel)
Lost RAM: 65,011K
Tuning: 192 (large 192), oom 184,320K, restore limit 61,440K (high-end-gfx)
Total PSS by category:
该标签下统计系统中所有进程每个类别占用内存的总和,具体每个的含义后面按app来解释。
用adb shell dumpsys meminfo <pid> 命令来获取单个进程对应的内存信息:
** MEMINFO in pid 5304 [personal.jayhou.meminfodemo] **
Pss Private Private SwapPss Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 2737 2668 0 0 9728 8103 1624
Dalvik Heap 635 576 0 0 2736 1200 1536
Dalvik Other 428 428 0 0
Stack 252 252 0 0
Ashmem 5 0 0 0
Other dev 14 0 12 0
.so mmap 1601 120 0 0
.apk mmap 564 0 0 0
.ttf mmap 59 0 0 0
.dex mmap 3987 4 2228 0
.oat mmap 306 0 0 0
.art mmap 4131 3800 88 0
Other mmap 13 4 0 0
Unknown 323 288 0 0
TOTAL 15055 8140 2328 0 12464 9303 3160
App Summary
Pss(KB)
------
Java Heap: 4464
Native Heap: 2668
Code: 2352
Stack: 252
Graphics: 0
Private Other: 732
System: 4587
TOTAL: 15055 TOTAL SWAP PSS: 0
Objects
Views: 17 ViewRootImpl: 1
AppContexts: 3 Activities: 1
Assets: 2 AssetManagers: 3
Local Binders: 9 Proxy Binders: 15
Parcel memory: 3 Parcel count: 12
Death Recipients: 0 OpenSSL Sockets: 0
WebViews: 0
SQL
MEMORY_USED: 0
PAGECACHE_OVERFLOW: 0 MALLOC_SIZE: 0
这个信息是在app进程中打印出来的,最终该命令会执行到对应进程的如下接口:
android/frameworks/base/core/java/android/app/ActivityThread.java
@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 {
dumpMemInfo(pw, mem, checkin, dumpFullInfo, dumpDalvik, dumpSummaryOnly, dumpUnreachable);
} finally {
pw.flush();
IoUtils.closeQuietly(pfd);
}
}
其中
Native Heap是指c 中malloc出来的堆空间
Dalvik Heap是指java中new出来的java堆空间
Pss Private Private SwapPss Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 2737 2668 0 0 9728 8103 1624
Dalvik Heap 635 576 0 0 2736 1200 1536
这里可以看到Native Heap Pss Total列是2737 也就是native代码中分配了2737KB的空间被占用 Heap Size列是9728,是指Native堆最大是这么多KB,后面还有Heap Alloc列是8103,这里是指在虚拟地址中分配了这么多空间,Dalvik Heap同理是指java中占用的空间,这里就奇怪了,Pss Total和Heap Allock为啥不一样呢?
用Demo来试验下
public class MainActivity extends AppCompatActivity {
private long[] m_8MB;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
m_8MB = new long[1024*1024];
for(int i = 0; i < 1024*1024/2; i++) {
m_8MB[i] = 0l;
}
}
}
如果没有for循环,只new long数组,不赋值,则:
** MEMINFO in pid 6652 [personal.jayhou.meminfodemo] **
Pss Private Private SwapPss Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 2678 2608 0 0 8704 7725 978
Dalvik Heap 587 528 0 0 15534 9390 6144
Dalvik Heap的HeapSize调整为了15MB左右,HeapAlloc增加了8MB,正好是new long数组占用的空间,但是注意Pss Total并没有受到影响。
如果加上for循环给long数组一半的元素赋值:
** MEMINFO in pid 7099 [personal.jayhou.meminfodemo] **
Pss Private Private SwapPss Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 2658 2584 0 0 8704 7720 983
Dalvik Heap 4687 4628 0 0 15534 9390 6144
可以看到Dalvik Heap下的Pss Total增加了4MB 其他没变。
所以Pss Total是指占用了真实的物理内存的空间,而Heap Alloc只是占用的虚拟内存的空间。是分配了空间,没有使用的那部分内存。
虚拟内存:进程空间内的虚拟内存地址,理论上32位cpu一个进程有4GB的虚拟内存可以使用。
物理内存:就是真正写的到内存条上的,真实地址对进程不可见,由操作系统把虚拟内存地址映射到物理内存地址。
其他部分:
Dalvik Other 428 428 0 0
Stack 252 252 0 0
Ashmem 5 0 0 0
Other dev 14 0 12 0
.so mmap 1601 120 0 0
.apk mmap 564 0 0 0
.ttf mmap 59 0 0 0
.dex mmap 3987 4 2228 0
.oat mmap 306 0 0 0
.art mmap 4131 3800 88 0
Other mmap 13 4 0 0
Unknown 323 288 0 0
上面这部分顾名思义 Stack是指运行中栈空间的使用(函数调用,局部变量等),Ashmem是匿名内存占用的空间,各种mmap是对应类型文件加载部分占用内存(可以部分加载也叫映射到内存)
要查看详细的内存占用,可以查看:
adb shell cat /proc/<pid>/smaps
例如刚才的进程7099实际使用的4MB内存:
cd3ff000-cdc00000 rw-p 00000000 00:01 105348 /dev/ashmem/dalvik-large object space allocation (deleted)
Size: 8196 kB
Rss: 4100 kB
Pss: 4100 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 4100 kB
Referenced: 4100 kB
Anonymous: 4100 kB
AnonHugePages: 0 kB
Swap: 0 kB
SwapPss: 0 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Locked: 0 kB
Size:指的就是分配了多少虚拟内存
Rss、Pss指的是实际物理内存使用的大小,由于这个内存段是纯new出来的,没有共享库,所以这两个值是一样的。由于只给4MB的数组赋值,操作系统只给分配了4MB的真实物理内存。
把/proc/<pid>/smaps 所有数据段中Pss部分加起来,和上面所有类型的Pss加起的TOTAL行的Pss Total值是一样的
TOTAL: 15055
其他部分:
App Summary
Pss(KB)
------
Java Heap: 4464
Native Heap: 2668
Code: 2352
Stack: 252
Graphics: 0
Private Other: 732
System: 4587
TOTAL: 15055 TOTAL SWAP PSS: 0
Objects
Views: 17 ViewRootImpl: 1
AppContexts: 3 Activities: 1
Assets: 2 AssetManagers: 3
Local Binders: 9 Proxy Binders: 15
Parcel memory: 3 Parcel count: 12
Death Recipients: 0 OpenSSL Sockets: 0
WebViews: 0
SQL
MEMORY_USED: 0
PAGECACHE_OVERFLOW: 0 MALLOC_SIZE: 0
App Summary部分换了一种方式统计Pss内存占用,其Total值和上面部分一致。具体含义可以参考如下代码:
android/frameworks/base/core/java/android/app/ActivityThread.java
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());
android/frameworks/base/core/java/android/os/Debug.java
/**
* 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
*/
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
*/
public int getSummaryNativeHeap() {
return nativePrivateDirty;
}
/**
* Pss of code and other static resource bytes in KB due to
* the application.
* @hide
*/
public int getSummaryCode() {
return getOtherPrivate(OTHER_SO)
+ getOtherPrivate(OTHER_JAR)
+ getOtherPrivate(OTHER_APK)
+ getOtherPrivate(OTHER_TTF)
+ getOtherPrivate(OTHER_DEX)
+ getOtherPrivate(OTHER_OAT);
}
/**
* 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
*/
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
*/
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
*/
public int getSummaryPrivateOther() {
return getTotalPrivateClean()
+ getTotalPrivateDirty()
- getSummaryJavaHeap()
- getSummaryNativeHeap()
- getSummaryCode()
- getSummaryStack()
- getSummaryGraphics();
}
/**
* Pss in KB due to the system.
* Notes:
* * Includes all shared memory.
* @hide
*/
public int getSummarySystem() {
return getTotalPss()
- getTotalPrivateClean()
- getTotalPrivateDirty();
}
Objects是统计App内部组件对象个数,其中Views、ViewRootImpl以及Activities个数,在Activity onDestroy后应该都会回收清零,如果onDestroy调用后这几个对象个数没有清零,就可能发生了内存泄漏。