Android 4.4 KitKat 提出了一个新系统服务,叫做procstats。它将帮助你更好的理解你的app是如何使用内存资源的。Procstats可以去监视你app在一段时间的行为,包括在后台(background)运行了多久,并在此段时间使用了多少内存。从而帮助你快速的找到应用中不效率和不规范的地方去避免影响其性能(performs),尤其是在低内存的设备上运行时。
你可以通过adb shell命令去使用procstats,或者更方便的方式是运行Process Stats开发者工具,它提供了一个前端的图形应用去展示和命令行相同的数据(在4.4版本的手机中点击Settings > Developer options > Process Stats)
查看全系统下的内存使用和后台进程(Looking at systemwide memory use and background processes)
如下图所示,当你打开Process Stats后,你可以看到一个在固定时间段内全系统内存使用情况和细节的简要分析。
在屏幕的最上面我们可以看到:
●最近3.5小时内的数据统计
●当前的设备内存处于良好状态("Device memory is currently normal")
●在这一整段时间内内存状态处于优(绿色的状态条),如果内存越来越少的话你将看到黄色和红色的状态条代表在这段时间内处于低内存状态。
在绿色状态条下面,我们可以看到后台进程和内存加载的概况
●右侧的百分数表示:每个进程所花费的时间占总时间的百分比
●蓝色条表示:每一个进程加载的相对内存计算(内存加载=runtime * vag_pss,后面会详细介绍)
●在显示过程中,有些应用会列出来多次(比如,Google Play services执行了两条进程)。这些APP的内存加载值是对于每个单独的进程的加载总数。
●有一些进程在最上面的进程他们都相对总时间执行了100%。但是他们有不同的比重,是因为相对内存使用的原因。
分析指定进程的内存(Analyzing memory for specific processes)
下面的例子展示了一些有趣的数据:
时钟APP相对于Google键盘消耗了更多的内存比重,即使它运行了更少的时间。我们可以通过点击他们去查看更多的细节信息。
通过两个进程的细节展示揭示了:
●为什么时钟在一直运行,因为当设备进入空闲状态时,它被用来当做屏保。
●即使时钟进程只运行了键盘进程的一半时间,但是它仍然十分明显的消耗了大量内存(接近3倍)。这也是为什么时钟内存的比重更大的原因。
从根本来说,procstats提供了一个“内存使用”的规格,去展示程序在后台的内存使用情况。类似与存储和数据使用规格但又不同于他们,因为内存更难去量化和测量(procstats使用了一些小花招做到了)。为了去阐述测量内存使用的复杂性,下面考虑下与其相关的话题:任务管理器(task managers)
了解任务管理和他们的内存信息(Understanding task managers and their memory info)
Android一直deeply支持多任务处理(multitasking),这意味着我们希望有一个类似与传统桌面UI一样的用户图形界面,去控制这些多重任务。
然而对比桌面操作系统的多任务处理,Android中从根本上来说更加的复杂(在Multitasking the Android Way这篇文章中有介绍)。
多任务和持续进程管理
为了去体会在Android中进程管理有什么不同,你可以通过使用命令行:adb shell dumpsys activity,去看一下重要的系统服务和Activity Manager的输出信息。
下面的例子展示了当前应用进程(Android 4.4),并按重要程度排列:
[plain]
1. ACTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes)
2. Process LRU list (sorted by oom_adj, 22 total, non-act at 2, non-svc at 2):
3. PERS #21: sys F/ /P trm: 0 23064:system/1000 (fixed)
4. PERS #20: pers F/ /P trm: 0 23163:com.android.systemui/u0a12 (fixed)
5. PERS #19: pers F/ /P trm: 0 23344:com.nuance.xt9.input/u0a77 (fixed)
6. PERS #18: pers F/ /P trm: 0 23357:com.android.phone/1001 (fixed)
7. PERS #17: pers F/ /P trm: 0 23371:com.android.nfc/1027 (fixed)
8. Proc # 3: fore F/ /IB trm: 0 13892:com.google.android.apps.magazines/u0a59 (service)
9. com.google.android.apps.magazines/com.google.apps.dots.android.app.service.SyncService<=Proc{23064:system/1000}
10. Proc # 2: fore F/ /IB trm: 0 23513:com.google.process.gapps/u0a8 (provider)
11. com.google.android.gsf/.gservices.GservicesProvider<=Proc{13892:com.google.android.apps.magazines/u0a59}
12. Proc # 0: fore F/A/T trm: 0 24811:com.android.settings/1000 (top-activity)
13. Proc # 4: vis F/ /IF trm: 0 23472:com.google.process.location/u0a8 (service)
14. com.google.android.backup/.BackupTransportService<=Proc{23064:system/1000}
15. Proc #14: prcp F/ /IF trm: 0 23298:com.google.android.inputmethod.latin/u0a57 (service)
16. com.google.android.inputmethod.latin/com.android.inputmethod.latin.LatinIME<=Proc{23064:system/1000}
17. Proc # 1: home B/ /HO trm: 0 23395:com.android.launcher/u0a13 (home)
18. Proc #16: cch B/ /CA trm: 0 23966:com.google.android.deskclock/u0a36 (cch-act)
19. Proc # 6: cch B/ /CE trm: 0 7716:com.google.android.music:main/u0a62 (cch-empty)
20. Proc # 5: cch B/ /CE trm: 0 8644:com.google.android.apps.docs/u0a39 (cch-empty)
21. Proc # 8: cch+2 B/ /CE trm: 0 5131:com.google.android.youtube/u0a78 (cch-empty)
22. Proc # 7: cch+2 B/ /CE trm: 0 23338:com.google.android.gms/u0a8 (cch-empty)
23. Proc #10: cch+4 B/ /CE trm: 0 8937:com.google.android.apps.walletnfcrel/u0a24 (cch-empty)
24. Proc # 9: cch+4 B/ /CE trm: 0 24689:com.google.android.apps.plus/u0a70 (cch-empty)
25. Proc #15: cch+6 B/ /S trm: 0 23767:com.google.android.apps.currents/u0a35 (cch-started-services)
26. Proc #13: cch+6 B/ /CE trm: 0 9115:com.google.android.gm/u0a44 (cch-empty)
27. Proc #12: cch+6 B/ /S trm: 0 7738:android.process.media/u0a6 (cch-started-services)
28. Proc #11: cch+6 B/ /CE trm: 0 8922:com.google.android.setupwizard/u0a19 (cch-empty)
dumpsys activity命令的输出信息,显示了所有现在正在运行的进程。
这里还有一些重要的进程组——持久系统进程(persistent system processes),前台系统进程(foreground processes),后台系统进程(background processes),最终缓存进程(finally cached processes)。
这些进程种类对于理解它是如何影响系统是非常重要的。
与此同时,进程列表是随时变化的。例如:在上面例子中我们可以看到“com.google.android.gm”(Gmail) 是当前很重要的进程,但是他是在后台同步运行,所以有些东西用户通常不会被告知或不想管理他。
每个进程的内存使用
传统的任务管理器和内存使用息息相关,Android提供了一个工具叫做meminfo,使用它可以去查看当前每一个进程的内存使用情况。你可以通过命令行去执行它:adb shell dumpsys meminfo
下面是一个使用它输出的例子:
[plain]
1. Total PSS by OOM adjustment:
2. 31841 kB: Native
3. 13173 kB: zygote (pid 23001)
4. 4372 kB: surfaceflinger (pid 23000)
5. 3721 kB: mediaserver (pid 126)
6. 3317 kB: glgps (pid 22993)
7. 1656 kB: drmserver (pid 125)
8. 995 kB: wpa_supplicant (pid 23148)
9. 786 kB: netd (pid 121)
10. 518 kB: sdcard (pid 132)
11. 475 kB: vold (pid 119)
12. 458 kB: keystore (pid 128)
13. 448 kB: /init (pid 1)
14. 412 kB: adbd (pid 134)
15. 254 kB: ueventd (pid 108)
16. 238 kB: dhcpcd (pid 10617)
17. 229 kB: tf_daemon (pid 130)
18. 200 kB: installd (pid 127)
19. 185 kB: dumpsys (pid 14207)
20. 144 kB: healthd (pid 117)
21. 139 kB: debuggerd (pid 122)
22. 121 kB: servicemanager (pid 118)
23. 48217 kB: System
24. 48217 kB: system (pid 23064)
25. 49095 kB: Persistent
26. 34012 kB: com.android.systemui (pid 23163 / activities)
27. 7719 kB: com.android.phone (pid 23357)
28. 4676 kB: com.android.nfc (pid 23371)
29. 2688 kB: com.nuance.xt9.input (pid 23344)
30. 24945 kB: Foreground
31. 24945 kB: com.android.settings (pid 24811 / activities)
32. 17136 kB: Visible
33. 14026 kB: com.google.process.location (pid 23472)
34. 3110 kB: com.android.defcontainer (pid 13976)
35. 6911 kB: Perceptible
36. 6911 kB: com.google.android.inputmethod.latin (pid 23298)
37. 14277 kB: A Services
38. 14277 kB: com.google.process.gapps (pid 23513)
39. 26422 kB: Home
40. 26422 kB: com.android.launcher (pid 23395 / activities)
41. 21798 kB: B Services
42. 16242 kB: com.google.android.apps.currents (pid 23767)
43. 5556 kB: android.process.media (pid 7738)
44. 145869 kB: Cached
45. 41588 kB: com.google.android.apps.plus (pid 24689)
46. 21417 kB: com.google.android.deskclock (pid 23966 / activities)
47. 14463 kB: com.google.android.apps.docs (pid 8644)
48. 14303 kB: com.google.android.gm (pid 9115)
49. 11014 kB: com.google.android.music:main (pid 7716)
50. 10688 kB: com.google.android.apps.magazines (pid 13892)
51. 10240 kB: com.google.android.gms (pid 23338)
52. 9882 kB: com.google.android.youtube (pid 5131)
53. 8807 kB: com.google.android.apps.walletnfcrel (pid 8937)
54. 3467 kB: com.google.android.setupwizard (pid 8922)
55.
56. Total RAM: 998096 kB
57. Free RAM: 574945 kB (145869 cached pss + 393200 cached + 35876 free)
58. Used RAM: 392334 kB (240642 used pss + 107196 buffers + 3856 shmem + 40640 slab)
59. Lost RAM: 30817 kB
60. Tuning: 64 (large 384), oom 122880 kB, restore limit 40960 kB (high-end-gfx)
dumpsys meminfo命令的输出例子,显示了当前正在运行的进程所使用的内存
我们正在看的和上面例子是同一个进程,并且也是按重要程度排序,不同的是这次有他们对内存影响。
通常当我们测量Android的使用内存时,我们使用Linux的PSS标准 (Proportional Set Size——实际物理内存)。
这是实际内存映射到进程中的数量,但是这个加权的的数量是在多个进程间共享的。因此如果以4K内存每一页(page)映射到两个进程中的话,那么PPS的数量就是每条进程2K。
使用PSS的好处是你可以添加这个值到所有的进程中去决定实际的总共内存使用。这个特性被用在meminfo报告的最后,去计算使用(Used)了多少内存(这部分来自于非缓存进程),相反的是空闲(free)大小(包含了缓存进程)。
任务管理器(Task-manager)样式的内存信息,展示在应用运行时的内存使用。
基于PSS的任务管理界面
当在设备上点击Settings > Apps > Running后可以显示任务管理器UI。
它显示了所有进程中运行的服务和系统状态,还计算了所有其他依赖进程的PPS内存。
此UI显示的都是即时的应用状态,而不是一段时间内的应用状态。因为在Android中,用户不能直接控制创建和清除应用进程。这些进程可能为了以后使用而保持当前状态,或当系统决定后清除,或者当用户没有明确开启他们时在后台运行。
所以这只是即时的内存使用状态,因此你可能会丢失在这一段时间内的一些重要信息。
例如,当我第一次查看进程状态时,可以看到com.google.android.apps.magazines进程在同步运行。但是就在我们搜集内存使用之后,它就不会在后台运行了,但是它仍然保持在旧的缓存进程中。
为了解决这个问题,就需要使用之前提过的procstats(它可以持续监视在一段时间内所有应用进程,从中收集PSS样品)。你可以通过命令行:adb shell dumpsys procstats去浏览procstats收集的原始数据
通过procstats查看一段时间内的内存使用
现在回到procstats,我们可以使用命令行:adb shell dumpsys procstats --hours 3 去输出最后3小时收集的内存信息。这些数据和之前图形界面的数据是相同的。
下面是所有进程在最近3小时内的输出数据,通过使用时间先后排序(在缓存状态下的进程不会计算在总时间排序中)。我们现在清晰的看到在这段时间内运行着一个庞大的进程组,中间又会一些新的进程突然的运行——这里包括Magazines进程,我们可以看到他相对于总时间3小时运行了3.6%。
[plain]
1. * com.google.android.inputmethod.latin / u0a57:
2. TOTAL: 100% (6.4MB-6.7MB-6.8MB/5.4MB-5.4MB-5.4MB over 21)
3. Imp Fg: 100% (6.4MB-6.7MB-6.8MB/5.4MB-5.4MB-5.4MB over 21)
4. * com.google.process.gapps / u0a8:
5. TOTAL: 100% (12MB-13MB-14MB/10MB-11MB-12MB over 211)
6. Imp Fg: 0.11%
7. Imp Bg: 0.83% (13MB-13MB-13MB/11MB-11MB-11MB over 1)
8. Service: 99% (12MB-13MB-14MB/10MB-11MB-12MB over 210)
9. * com.android.systemui / u0a12:
10. TOTAL: 100% (29MB-32MB-34MB/26MB-29MB-30MB over 21)
11. Persistent: 100% (29MB-32MB-34MB/26MB-29MB-30MB over 21)
12. * com.android.phone / 1001:
13. TOTAL: 100% (6.5MB-7.1MB-7.6MB/5.4MB-5.9MB-6.4MB over 21)
14. Persistent: 100% (6.5MB-7.1MB-7.6MB/5.4MB-5.9MB-6.4MB over 21)
15. * com.nuance.xt9.input / u0a77:
16. TOTAL: 100% (2.3MB-2.5MB-2.7MB/1.5MB-1.5MB-1.5MB over 21)
17. Persistent: 100% (2.3MB-2.5MB-2.7MB/1.5MB-1.5MB-1.5MB over 21)
18. * com.android.nfc / 1027:
19. TOTAL: 100% (4.2MB-4.5MB-4.6MB/3.2MB-3.2MB-3.3MB over 21)
20. Persistent: 100% (4.2MB-4.5MB-4.6MB/3.2MB-3.2MB-3.3MB over 21)
21. * com.google.process.location / u0a8:
22. TOTAL: 100% (13MB-13MB-14MB/10MB-11MB-11MB over 21)
23. Imp Fg: 100% (13MB-13MB-14MB/10MB-11MB-11MB over 21)
24. * system / 1000:
25. TOTAL: 100% (42MB-46MB-56MB/39MB-42MB-48MB over 21)
26. Persistent: 100% (42MB-46MB-56MB/39MB-42MB-48MB over 21)
27. * com.google.android.apps.currents / u0a35:
28. TOTAL: 100% (16MB-16MB-16MB/14MB-14MB-14MB over 17)
29. Service: 100% (16MB-16MB-16MB/14MB-14MB-14MB over 17)
30. * com.android.launcher / u0a13:
31. TOTAL: 77% (25MB-26MB-27MB/22MB-23MB-24MB over 73)
32. Top: 77% (25MB-26MB-27MB/22MB-23MB-24MB over 73)
33. (Home): 23% (25MB-26MB-26MB/23MB-23MB-24MB over 12)
34. * android.process.media / u0a6:
35. TOTAL: 48% (5.0MB-5.3MB-5.5MB/4.0MB-4.2MB-4.2MB over 11)
36. Imp Fg: 0.00%
37. Imp Bg: 0.00%
38. Service: 48% (5.0MB-5.3MB-5.5MB/4.0MB-4.2MB-4.2MB over 11)
39. Receiver: 0.00%
40. (Cached): 22% (4.1MB-4.5MB-4.8MB/3.0MB-3.5MB-3.8MB over 8)
41. * com.google.android.deskclock / u0a36:
42. TOTAL: 42% (20MB-21MB-21MB/18MB-19MB-19MB over 8)
43. Imp Fg: 42% (20MB-21MB-21MB/18MB-19MB-19MB over 8)
44. Service: 0.00%
45. Receiver: 0.01%
46. (Cached): 58% (17MB-20MB-21MB/16MB-18MB-19MB over 14)
47. * com.android.settings / 1000:
48. TOTAL: 23% (19MB-22MB-28MB/15MB-19MB-24MB over 31)
49. Top: 23% (19MB-22MB-28MB/15MB-19MB-24MB over 31)
50. (Last Act): 77% (9.7MB-14MB-20MB/7.5MB-11MB-18MB over 8)
51. (Cached): 0.02%
52. * com.google.android.apps.magazines / u0a59:
53. TOTAL: 3.6% (10MB-10MB-10MB/8.7MB-9.0MB-9.0MB over 6)
54. Imp Bg: 0.03%
55. Service: 3.6% (10MB-10MB-10MB/8.7MB-9.0MB-9.0MB over 6)
56. (Cached): 17% (9.9MB-10MB-10MB/8.7MB-8.9MB-9.0MB over 5)
57. * com.android.defcontainer / u0a5:
58. TOTAL: 1.4% (2.7MB-3.0MB-3.0MB/1.9MB-1.9MB-1.9MB over 7)
59. Top: 1.2% (3.0MB-3.0MB-3.0MB/1.9MB-1.9MB-1.9MB over 6)
60. Imp Fg: 0.19% (2.7MB-2.7MB-2.7MB/1.9MB-1.9MB-1.9MB over 1)
61. Service: 0.00%
62. (Cached): 15% (2.6MB-2.6MB-2.6MB/1.8MB-1.8MB-1.8MB over 1)
63. * com.google.android.youtube / u0a78:
64. TOTAL: 1.3% (9.0MB-9.0MB-9.0MB/7.8MB-7.8MB-7.8MB over 1)
65. Imp Bg: 1.0% (9.0MB-9.0MB-9.0MB/7.8MB-7.8MB-7.8MB over 1)
66. Service: 0.27%
67. Service Rs: 0.01%
68. Receiver: 0.00%
69. (Cached): 99% (9.1MB-9.4MB-9.7MB/7.7MB-7.9MB-8.1MB over 24)
70. * com.google.android.gms / u0a8:
71. TOTAL: 0.91% (9.2MB-9.2MB-9.2MB/7.6MB-7.6MB-7.6MB over 1)
72. Imp Bg: 0.79% (9.2MB-9.2MB-9.2MB/7.6MB-7.6MB-7.6MB over 1)
73. Service: 0.11%
74. Receiver: 0.00%
75. (Cached): 99% (8.2MB-9.4MB-10MB/6.5MB-7.6MB-8.1MB over 25)
76. * com.google.android.gm / u0a44:
77. TOTAL: 0.56%
78. Imp Bg: 0.55%
79. Service: 0.01%
80. Receiver: 0.00%
81. (Cached): 99% (11MB-13MB-14MB/10MB-12MB-13MB over 24)
82. * com.google.android.apps.plus / u0a70:
83. TOTAL: 0.22%
84. Imp Bg: 0.22%
85. Service: 0.00%
86. Receiver: 0.00%
87. (Cached): 100% (38MB-40MB-41MB/36MB-38MB-39MB over 17)
88. * com.google.android.apps.docs / u0a39:
89. TOTAL: 0.15%
90. Imp Bg: 0.09%
91. Service: 0.06%
92. (Cached): 54% (13MB-14MB-14MB/12MB-12MB-13MB over 17)
93. * com.google.android.music:main / u0a62:
94. TOTAL: 0.11%
95. Imp Bg: 0.04%
96. Service: 0.06%
97. Receiver: 0.01%
98. (Cached): 70% (7.7MB-10MB-11MB/6.4MB-9.0MB-9.3MB over 20)
99. * com.google.android.apps.walletnfcrel / u0a24:
100. TOTAL: 0.01%
101. Receiver: 0.01%
102. (Cached): 69% (8.1MB-8.4MB-8.6MB/7.0MB-7.1MB-7.1MB over 13)
103. * com.google.android.setupwizard / u0a19:
104. TOTAL: 0.00%
105. Receiver: 0.00%
106. (Cached): 69% (2.7MB-3.2MB-3.4MB/1.8MB-2.0MB-2.2MB over 13)
107.
108. Run time Stats:
109. SOff/Norm: +1h43m29s710ms
110. SOn /Norm: +1h37m14s290ms
111. TOTAL: +3h20m44s0ms
112.
113. Start time: 2013-11-06 07:24:27
114. Total elapsed time: +3h42m23s56ms (partial) libdvm.so chromeview
命令行:dumpsys procstats --hours 3的输出例子,显示最近3小时后台运行的进程内存细节。
这个百分比告诉你在总时间内各种状态下进程的消耗。minPss-avgPss-maxPss / minUss-avgUss-maxUss。procstats 工具有一组命令行选项去控制输出结果——使用:adb shell dumpsys procstats -h查看可执行选项列表。
对比这些从procstats得到的原数据我们可以看到,进程会在下面的几种状态中运行: Imp Fg, Imp Bg, Service, Service Rs, and Receiver。
在这些情况下,进程会活跃的运行在后台中,只要它完成它需要做的工作。在设备使用内存时,这些进程状态最容易引起下面这个问题问题:应用在后台运行时,内存从事其他工作。
开始使用procstats(Getting started with procstats)
我们已经找到新的procstats工具去了解整体内存在Android系统中的行为。这也是Android4.4的工程减肥计划(Project Svelte)的重要部分之一。
在开发你自己的应用时,一定要使用procstats和刚才提到的其他工具去帮助你了解APP的表现,特别是他在后台运行的时间和使用的内存。