1.车机原始数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
Applications Memory Usage (in Kilobytes):
Uptime: 17677236 Realtime: 17677236

Total PSS by process:
199,233K: com.iflytek.autofly.mediax (pid 2782 / activities)
124,496K: system (pid 792)
106,727K: com.gxa.service.systemui (pid 1513)
91,710K: com.gxa.cockpit.hvac (pid 1370)
41,384K: zygote (pid 361)
40,611K: com.android.systemui (pid 1243 / activities)
37,332K: com.gxatek.cockpit.settings (pid 30089 / activities)
36,911K: com.gxatek.cockpit.msgcenter (pid 2469)
35,512K: com.iflytek.autofly.avatar (pid 2501)
24,970K: com.iflytek.demo (pid 15276)
23,248K: com.gxatek.cockpit.miniprogram (pid 3481)
22,704K: com.iflytek.autofly.systemserver (pid 15103)
21,398K: com.gac.cloud.app (pid 2567)
19,822K: com.android.webview:sandboxed_process0 (pid 3027)
19,732K: com.gxatek.cockpit.scenesengine (pid 3511)
19,089K: com.gxa.service.account (pid 1381)
18,990K: com.android.bluetooth (pid 1099)
18,863K: com.gxatek.cockpit.screensaver (pid 4057)
18,783K: platformservice.Services (pid 2397)
18,384K: zygote64 (pid 360)
18,246K: webview_zygote (pid 1145)
18,158K: com.android.commands.monkey (pid 30761)
17,505K: surfaceflinger (pid 485)
17,272K: com.android.car (pid 1772)
16,549K: libweexjsb.so (pid 3533)
15,683K: android.hardware.audio@2.0-service (pid 440)
15,565K: com.gxa.service.mediacenterservice (pid 1552)
15,563K: com.gxa.firewall (pid 1992 / activities)
14,053K: com.desaysv.vehiclelan.proxy@1.0-service (pid 309)
13,668K: audioserver (pid 480)
......

Total PSS by OOM adjustment:
405,214K: Native
41,384K: zygote (pid 361)
24,970K: com.iflytek.demo (pid 15276)
18,384K: zygote64 (pid 360)
18,246K: webview_zygote (pid 1145)
18,158K: com.android.commands.monkey (pid 30761)
17,505K: surfaceflinger (pid 485)
16,549K: libweexjsb.so (pid 3533)
15,683K: android.hardware.audio@2.0-service (pid 440)
14,053K: com.desaysv.vehiclelan.proxy@1.0-service (pid 309)
13,668K: audioserver (pid 480)
10,475K: android.hardware.graphics.composer@2.2-service (pid 452)
9,317K: media.codec (pid 605)
8,729K: android.hardware.broadcastradio@2.0-service.g6 (pid 446)
7,757K: android.hardware.wifi@1.0-service (pid 463)
7,662K: caraudioserver (pid 588)
......
124,496K: System
124,496K: system (pid 792)
495,619K: Persistent
106,727K: com.gxa.service.systemui (pid 1513)
40,611K: com.android.systemui (pid 1243 / activities)
36,911K: com.gxatek.cockpit.msgcenter (pid 2469)
35,512K: com.iflytek.autofly.avatar (pid 2501)
21,398K: com.gac.cloud.app (pid 2567)
17,272K: com.android.car (pid 1772)
15,565K: com.gxa.service.mediacenterservice (pid 1552)
15,563K: com.gxa.firewall (pid 1992 / activities)
12,893K: com.gxa.appservice.caradapter (pid 1581)
12,663K: com.android.phone (pid 9965)
11,964K: com.android.car.hvac (pid 2508)
11,572K: com.gxatek.cockpit.carlife (pid 2483)
11,434K: com.gxa.service.ebcall (pid 1676)
11,429K: com.gxa.service.settings (pid 1423)
10,935K: com.desaysv.otaservice (pid 2575)
......
18,990K: Persistent Service
18,990K: com.android.bluetooth (pid 1099)
12,852K: Foreground
12,852K: com.iflytek.autofly.naviselect (pid 15011 / activities)
521,294K: Visible
199,233K: com.iflytek.autofly.mediax (pid 2782 / activities)
91,710K: com.gxa.cockpit.hvac (pid 1370)
22,704K: com.iflytek.autofly.systemserver (pid 15103)
19,822K: com.android.webview:sandboxed_process0 (pid 3027)
19,089K: com.gxa.service.account (pid 1381)
18,863K: com.gxatek.cockpit.screensaver (pid 4057)
18,783K: platformservice.Services (pid 2397)
12,914K: com.gxa.car.power (pid 1451)
12,868K: com.gxa.car.externalkey (pid 1354)
10,901K: com.gac.cloud.cert (pid 2718)
10,798K: com.gxa.car.engineMode (pid 1405)
10,590K: com.gxa.appservice.platformadapter.adaptermainservice (pid 2643)
10,217K: com.gxa.service.ota (pid 2818)
9,010K: com.gxa.car.procmanagement (pid 2802)
8,462K: com.gxa.service.cluster (pid 1645)
8,352K: com.gxa.car.hardkey (pid 2389)
8,141K: android.ext.services (pid 3252)
7,989K: android.ext.services (pid 3267)
7,550K: com.iflytek.autofly.accountcenter (pid 3087)
7,528K: android.ext.services (pid 2137)
5,770K: com.gxatek.appservice.cluster (pid 3071)
59,002K: Perceptible
23,248K: com.gxatek.cockpit.miniprogram (pid 3481)
19,732K: com.gxatek.cockpit.scenesengine (pid 3511)
8,066K: com.android.inputmethod.latin (pid 2109)
7,956K: com.desaysv.ftpserver (pid 3620)
18,531K: A Services
9,298K: com.iflytek.autofly.mediax:remote (pid 10360)
9,233K: com.iflytek.autofly.mediax:remote (pid 10857)
29,298K: B Services
8,706K: com.iflytek.autofly.mediax:remote (pid 3104)
7,016K: android.process.media (pid 23822)
5,697K: com.android.car.messenger (pid 3834)
4,040K: com.qti.diagservices (pid 3707)
3,839K: com.qualcomm.qcrilmsgtunnel (pid 2332)
96,280K: Cached
37,332K: com.gxatek.cockpit.settings (pid 30089 / activities)
8,291K: com.iflytek.autofly.accountcenter (pid 10320)
8,207K: com.iflytek.autofly.accountcenter (pid 10818)
7,960K: com.google.android.car.vms.subscriber (pid 14643)
5,333K: com.gxatek.appservice.cluster (pid 10803)
5,074K: com.gxatek.appservice.cluster (pid 11980)
4,452K: com.android.mtp (pid 31084)
4,270K: com.android.mtp (pid 5760)
4,192K: com.android.mtp (pid 5685)
3,829K: com.android.externalstorage (pid 31047)
3,674K: com.android.externalstorage (pid 5445)
3,666K: com.android.externalstorage (pid 5430)

Total PSS by category:
645,466K: Native
257,284K: .so mmap
193,418K: .dex mmap
140,979K: Dalvik
112,332K: .art mmap
103,828K: Unknown
98,915K: .apk mmap
75,275K: .jar mmap
48,058K: Other mmap
42,943K: Dalvik Other
37,704K: .oat mmap
13,094K: .ttf mmap
7,928K: Stack
3,206K: Other dev
1,146K: Ashmem
0K: Cursor
0K: Gfx dev
0K: EGL mtrack
0K: GL mtrack
0K: Other mtrack

Total RAM: 6,266,692K (status normal)
Free RAM: 3,487,696K ( 96,280K cached pss + 844,924K cached kernel + 2,546,492K free)
Used RAM: 2,162,420K (1,685,296K used pss + 477,124K kernel)
Lost RAM: 616,576K
Tuning: 512 (large 512), oom 322,560K, restore limit 107,520K (high-end-gfx)

2.追根溯源

简单的查看内存信息可以使用命令:adb shell dumpsys meminfo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 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接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// android/frameworks/base/services/core/java/com/android/server/am/ ActivityManagerService.java
public void setSystemProcess() {
try {
ServiceManager.addService(Context.ACTIVITY_SERVICE, this, /* allowIsolated= */ true,DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
// 添加meminfo服务到ServiceManager中
ServiceManager.addService("meminfo", new MemBinder(this), /* allowIsolated= */ false,DUMP_FLAG_PRIORITY_HIGH);
ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
ServiceManager.addService("dbinfo", new DbBinder(this));
if (MONITOR_CPU_USAGE) {
ServiceManager.addService("cpuinfo", new CpuBinder(this),/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
}
ServiceManager.addService("permission", new PermissionController(this));
ServiceManager.addService("processinfo", new ProcessInfoService(this));

......
}
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(
"Unable to find android system package", e);
}
}

从上面代码中可以看到AMS将各种服务通过setSystemProcess()方法注册到ServiceManager中,我们本次要分析的是meminfo,因此主要看MemBinder即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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) {
// 最终会调到AMS的dumpApplicationMemoryUsage方法打印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);
}
}

从上面代码可以看出,dumpsys meminfo最终会调到AMS的dumpApplicationMemoryUsage中,然后经过一系列对传入参数的判断,打印出第一小节的内容。

3.解析打印内容

3.1 标题解析
1
2
3
4
5
6
7
8
9
10
11
Applications Memory Usage (in Kilobytes):
Uptime: 17677236 Realtime: 17677236

Total PSS by process:
......

Total PSS by OOM adjustment:
......

Total PSS by category:
......
  • 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时的优先级,从高到低排列(Native最高,Cached最低)。OOM_ADJ值越小,优先级越高,越难被lowmem kill杀。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// frameworks/base/services/core/java/com/android/server/am/ProcessList.java

// Uninitialized value for any major or minor adj fields
static final int INVALID_ADJ = -10000;

// 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;
static final int VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1;

// 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;

由于在Cached标签下的进程是随时可以回收内存的缓存进程,所以该部分内存在后面会统计到Free RAM字段中

1
2
3
4
5
Total RAM: 6,266,692K (status normal)
Free RAM: 3,487,696K ( 96,280K cached pss + 844,924K cached kernel + 2,546,492K free)
Used RAM: 2,162,420K (1,685,296K used pss + 477,124K kernel)
Lost RAM: 616,576K
Tuning: 512 (large 512), oom 322,560K, restore limit 107,520K (high-end-gfx)
3.2 单个app内存分析

Total PSS by category:该标签下统计系统中所有进程每个类别占用内存的总和,具体每个的含义后面按app来解释。

1
2
# 获取单个进程对应的内存信息:
adb shell dumpsys meminfo <pid>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
** MEMINFO in pid 5304 [com.gxa.car.scene] **
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进程中打印出来的,最终该命令会执行到对应进程的如下接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 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);
}
}
3.2.1 细节分析
1
2
3
4
5
6
** MEMINFO in pid 5304 [com.gxa.car.scene] **
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是指c/c++ 中malloc出来的堆空间
Dalvik Heap是指java中new出来的java堆空间

这里可以看到Native Heap Pss Total列是2737 也就是native代码中分配了2737KB的空间被占用 Heap Size列是9728,是指Native堆最大是这么多KB,后面还有Heap Alloc列是8103,这里是指在虚拟地址中分配了这么多空间,Dalvik Heap同理是指java中占用的空间。

其中Pss Total是指占用了真实的物理内存的空间,而Heap Alloc只是占用的虚拟内存的空间。是分配了空间,没有使用的那部分内存。

例如:

此时只在虚拟内存空间增加了8M大小
private long[] space = new long[1024*1024];

如果给space赋值了,就会在真实的物理空间增加大小

for (int i = 0; i < 1024 * 1024; i++) {
​ space[i] = 01;
}

虚拟内存:进程空间内的虚拟内存地址,理论上32位cpu一个进程有4GB的虚拟内存可以使用。
物理内存:就是真正写的到内存条上的,真实地址对进程不可见,由操作系统把虚拟内存地址映射到物理内存地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
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

其他的细节信息就是 Stack是指运行中栈空间的使用(函数调用,局部变量等),Ashmem是匿名内存占用的空间,各种mmap是对应类型文件加载部分占用内存(可以部分加载也叫映射到内存)

1
2
# 查看某个进程内存详情
adb shell cat /proc/<pid>/smaps
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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的真实物理内存。

3.2.2 另一种方式统计应用内存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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值和上面部分一致。可以通过App Summary查看到创建了多少对象,应用对象一段事件内是否没有正常释放导致内存溢出的情况。具体含义可以参考如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 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());