dumpsys
是一种在 Android 设备上运行的工具,用于提供有关系统服务的信息。使用 Android 调试桥 (ADB) 从命令行调用 dumpsys
,以获取连接设备上所有正在运行的系统服务的诊断输出。
此输出通常比您想要的冗长,因此请使用此页面上的 命令行选项 获取仅您所需的系统服务的输出。此页面还介绍了如何使用 dumpsys
来完成常见任务,例如检查输入、RAM、电池或网络诊断。
语法
使用 dumpsys
的通用语法如下所示
adb shell dumpsys [-t timeout] [--help | -l | --skip services | service [arguments] | -c | -h]
要获取连接设备所有系统服务的诊断输出,请运行 adb shell dumpsys
。但是,这会输出比您通常想要的信息多得多的信息。为了获得更易于管理的输出,请在命令中指定要检查的服务。例如,以下命令提供输入组件(如触摸屏或内置键盘)的系统数据
adb shell dumpsys input
要获取可与 dumpsys
一起使用的系统服务的完整列表,请使用以下命令
adb shell dumpsys -l
命令行选项
下表列出了使用 dumpsys
时的可用选项
选项 | 描述 |
---|---|
-t timeout
|
指定超时时间(以秒为单位)。未指定时,默认值为 10 秒。 |
--help
|
打印 dumpsys 工具的帮助文本。 |
-l
|
输出可与 dumpsys 一起使用的系统服务的完整列表。 |
--skip services
|
指定不想包含在输出中的 services。 |
service [arguments]
|
指定要输出的 service。某些服务可能允许您传递可选的 arguments。要了解这些可选参数,请将 -h 选项与服务一起传递adb shell dumpsys procstats -h |
-c
|
在指定某些服务时,附加此选项以机器友好的格式输出数据。 |
-h
|
对于某些服务,附加此选项以查看帮助文本以及该服务的其他选项。 |
检查输入诊断
指定 input
服务(如以下命令所示)会转储系统输入设备(如键盘和触摸屏)的状态以及输入事件的处理过程。
adb shell dumpsys input
输出因连接设备上运行的 Android 版本而异。以下部分介绍了您通常会看到的信息类型。
事件中心状态
以下是检查输入诊断的事件中心状态时可能看到的内容示例
INPUT MANAGER (dumpsys input) Event Hub State: BuiltInKeyboardId: -2 Devices: -1: Virtual Classes: 0x40000023 Path:Descriptor: a718a782d34bc767f4689c232d64d527998ea7fd Location: ControllerNumber: 0 UniqueId: Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000 KeyLayoutFile: /system/usr/keylayout/Generic.kl KeyCharacterMapFile: /system/usr/keychars/Virtual.kcm ConfigurationFile: HaveKeyboardLayoutOverlay: false 1: msm8974-taiko-mtp-snd-card Headset Jack Classes: 0x00000080 Path: /dev/input/event5 Descriptor: c8e3782483b4837ead6602e20483c46ff801112c Location: ALSA ControllerNumber: 0 UniqueId: Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000 KeyLayoutFile: KeyCharacterMapFile: ConfigurationFile: HaveKeyboardLayoutOverlay: false 2: msm8974-taiko-mtp-snd-card Button Jack Classes: 0x00000001 Path: /dev/input/event4 Descriptor: 96fe62b244c555351ec576b282232e787fb42bab Location: ALSA ControllerNumber: 0 UniqueId: Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000 KeyLayoutFile: /system/usr/keylayout/msm8974-taiko-mtp-snd-card_Button_Jack.kl KeyCharacterMapFile: /system/usr/keychars/msm8974-taiko-mtp-snd-card_Button_Jack.kcm ConfigurationFile: HaveKeyboardLayoutOverlay: false 3: hs_detect Classes: 0x00000081 Path: /dev/input/event3 Descriptor: 485d69228e24f5e46da1598745890b214130dbc4 Location: ControllerNumber: 0 UniqueId: Identifier: bus=0x0000, vendor=0x0001, product=0x0001, version=0x0001 KeyLayoutFile: /system/usr/keylayout/hs_detect.kl KeyCharacterMapFile: /system/usr/keychars/hs_detect.kcm ConfigurationFile: HaveKeyboardLayoutOverlay: false ...
输入读取器状态
InputReader
负责从内核解码输入事件。其状态转储显示有关每个输入设备配置方式以及发生的最近状态更改(例如按键或触摸屏上的触摸)的信息。
以下示例显示了触摸屏的输出。请注意有关设备分辨率和所用校准参数的信息。
Input Reader State ... Device 6: Melfas MMSxxx Touchscreen IsExternal: false Sources: 0x00001002 KeyboardType: 0 Motion Ranges: X: source=0x00001002, min=0.000, max=719.001, flat=0.000, fuzz=0.999 Y: source=0x00001002, min=0.000, max=1279.001, flat=0.000, fuzz=0.999 PRESSURE: source=0x00001002, min=0.000, max=1.000, flat=0.000, fuzz=0.000 SIZE: source=0x00001002, min=0.000, max=1.000, flat=0.000, fuzz=0.000 TOUCH_MAJOR: source=0x00001002, min=0.000, max=1468.605, flat=0.000, fuzz=0.000 TOUCH_MINOR: source=0x00001002, min=0.000, max=1468.605, flat=0.000, fuzz=0.000 TOOL_MAJOR: source=0x00001002, min=0.000, max=1468.605, flat=0.000, fuzz=0.000 TOOL_MINOR: source=0x00001002, min=0.000, max=1468.605, flat=0.000, fuzz=0.000 Touch Input Mapper: Parameters: GestureMode: spots DeviceType: touchScreen AssociatedDisplay: id=0, isExternal=false OrientationAware: true Raw Touch Axes: X: min=0, max=720, flat=0, fuzz=0, resolution=0 Y: min=0, max=1280, flat=0, fuzz=0, resolution=0 Pressure: min=0, max=255, flat=0, fuzz=0, resolution=0 TouchMajor: min=0, max=30, flat=0, fuzz=0, resolution=0 TouchMinor: unknown range ToolMajor: unknown range ToolMinor: unknown range Orientation: unknown range Distance: unknown range TiltX: unknown range TiltY: unknown range TrackingId: min=0, max=65535, flat=0, fuzz=0, resolution=0 Slot: min=0, max=9, flat=0, fuzz=0, resolution=0 Calibration: touch.size.calibration: diameter touch.size.scale: 10.000 touch.size.bias: 0.000 touch.size.isSummed: false touch.pressure.calibration: amplitude touch.pressure.scale: 0.005 touch.orientation.calibration: none touch.distance.calibration: none SurfaceWidth: 720px SurfaceHeight: 1280px SurfaceOrientation: 0 Translation and Scaling Factors: XScale: 0.999 YScale: 0.999 XPrecision: 1.001 YPrecision: 1.001 GeometricScale: 0.999 PressureScale: 0.005 SizeScale: 0.033 OrientationCenter: 0.000 OrientationScale: 0.000 DistanceScale: 0.000 HaveTilt: false TiltXCenter: 0.000 TiltXScale: 0.000 TiltYCenter: 0.000 TiltYScale: 0.000 Last Button State: 0x00000000 Last Raw Touch: pointerCount=0 Last Cooked Touch: pointerCount=0
在输入读取器状态转储的末尾,有一些关于全局配置参数的信息,例如点击间隔
Configuration: ExcludedDeviceNames: [] VirtualKeyQuietTime: 0.0ms PointerVelocityControlParameters: scale=1.000, lowThreshold=500.000, highThreshold=3000.000, acceleration=3.000 WheelVelocityControlParameters: scale=1.000, lowThreshold=15.000, highThreshold=50.000, acceleration=4.000 PointerGesture: Enabled: true QuietInterval: 100.0ms DragMinSwitchSpeed: 50.0px/s TapInterval: 150.0ms TapDragInterval: 300.0ms TapSlop: 20.0px MultitouchSettleInterval: 100.0ms MultitouchMinDistance: 15.0px SwipeTransitionAngleCosine: 0.3 SwipeMaxWidthRatio: 0.2 MovementSpeedRatio: 0.8 ZoomSpeedRatio: 0.3
输入分发器状态
InputDispatcher
负责将输入事件发送到应用。如以下示例输出所示,其状态转储显示有关哪个窗口被触摸、输入队列的状态、是否正在进行 ANR 以及其他输入事件信息
Input Dispatcher State: DispatchEnabled: 1 DispatchFrozen: 0 FocusedApplication: <null> FocusedWindow: name='Window{3fb06dc3 u0 StatusBar}' TouchStates: <no displays touched> Windows: 0: name='Window{357bbbfe u0 SearchPanel}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=false, canReceiveKeys=false, flags=0x01820100, type=0x000007e8, layer=211000, frame=[0,0][1080,1920], scale=1.000000, touchableRegion=[0,0][1080,1920], inputFeatures=0x00000000, ownerPid=22674, ownerUid=10020, dispatchingTimeout=5000.000ms 1: name='Window{3b14c0ca u0 NavigationBar}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=false, canReceiveKeys=false, flags=0x01840068, type=0x000007e3, layer=201000, frame=[0,1776][1080,1920], scale=1.000000, touchableRegion=[0,1776][1080,1920], inputFeatures=0x00000000, ownerPid=22674, ownerUid=10020, dispatchingTimeout=5000.000ms 2: name='Window{2c7e849c u0 com.vito.lux}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=true, canReceiveKeys=false, flags=0x0089031a, type=0x000007d6, layer=191000, frame=[-495,-147][1575,1923], scale=1.000000, touchableRegion=[-495,-147][1575,1923], inputFeatures=0x00000000, ownerPid=4697, ownerUid=10084, dispatchingTimeout=5000.000ms ... MonitoringChannels: 0: 'WindowManager (server)' RecentQueue: length=10 MotionEvent(deviceId=4, source=0x00001002, action=2, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, edgeFlags=0x00000000, xPrecision=1.0, yPrecision=1.0, displayId=0, pointers=[0: (335.0, 1465.0)]), policyFlags=0x62000000, age=217264.0ms MotionEvent(deviceId=4, source=0x00001002, action=1, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, edgeFlags=0x00000000, xPrecision=1.0, yPrecision=1.0, displayId=0, pointers=[0: (335.0, 1465.0)]), policyFlags=0x62000000, age=217255.7ms MotionEvent(deviceId=4, source=0x00001002, action=0, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, edgeFlags=0x00000000, xPrecision=1.0, yPrecision=1.0, displayId=0, pointers=[0: (330.0, 1283.0)]), policyFlags=0x62000000, age=216805.0ms ... PendingEvent: <none> InboundQueue: <empty> ReplacedKeys: <empty> Connections: 0: channelName='WindowManager (server)', windowName='monitor', status=NORMAL, monitor=true, inputPublisherBlocked=false OutboundQueue: <empty> WaitQueue: <empty> 1: channelName='278c1d65 KeyguardScrim (server)', windowName='Window{278c1d65 u0 KeyguardScrim}', status=NORMAL, monitor=false, inputPublisherBlocked=false OutboundQueue: <empty> WaitQueue: <empty> 2: channelName='357bbbfe SearchPanel (server)', windowName='Window{357bbbfe u0 SearchPanel}', status=NORMAL, monitor=false, inputPublisherBlocked=false OutboundQueue: <empty> WaitQueue: <empty> ... AppSwitch: not pending 7: channelName='2280455f com.google.android.gm/com.google.android.gm.ConversationListActivityGmail (server)', windowName='Window{2280455f u0 com.google.android.gm/com.google.android.gm.ConversationListActivityGmail}', status=NORMAL, monitor=false, inputPublisherBlocked=false OutboundQueue: <empty> WaitQueue: <empty> 8: channelName='1a7be08a com.android.systemui/com.android.systemui.recents.RecentsActivity (server)', windowName='Window{1a7be08a u0 com.android.systemui/com.android.systemui.recents.RecentsActivity EXITING}', status=NORMAL, monitor=false, inputPublisherBlocked=false OutboundQueue: <empty> WaitQueue: <empty> 9: channelName='3b14c0ca NavigationBar (server)', windowName='Window{3b14c0ca u0 NavigationBar}', status=NORMAL, monitor=false, inputPublisherBlocked=false OutboundQueue: <empty> WaitQueue: <empty> ... Configuration: KeyRepeatDelay: 50.0ms KeyRepeatTimeout: 500.0ms
需要检查的事项
以下列出了检查 input
服务的输出时需要考虑的事项
事件中心状态
- 所有您期望的输入设备都存在。
- 每个输入设备都具有相应的键布局文件、键字符映射文件和输入设备配置文件。如果文件丢失或包含语法错误,则不会加载它们。
- 每个输入设备都已正确分类。
Classes
字段中的位对应于EventHub.h
中的标志,例如INPUT_DEVICE_CLASS_TOUCH_MT
。 BuiltInKeyboardId
正确。如果设备没有内置键盘,则 ID 必须为-2
。否则,它应该是内置键盘的 ID。- 如果您观察到
BuiltInKeyboardId
不是-2
但应该是,则缺少特殊功能键盘的键字符映射文件。特殊功能键盘设备应具有仅包含行type SPECIAL_FUNCTION
的键字符映射文件。
输入读取器状态
- 所有预期的输入设备都存在。
- 每个输入设备都已正确配置。特别是,请检查触摸屏和操纵杆轴是否正确。
输入分发器状态
- 所有输入事件都按预期处理。
- 同时触摸触摸屏并运行
dumpsys
后,TouchStates
行会正确识别您正在触摸的窗口。
测试UI性能
指定gfxinfo
服务可提供与录制阶段发生的动画帧相关的性能信息输出。以下命令使用gfxinfo
收集指定包名的UI性能数据
adb shell dumpsys gfxinfo package-name
您还可以包含framestats
选项以提供最近帧的更详细的帧时序信息,以便您可以更准确地跟踪和调试问题
adb shell dumpsys gfxinfo package-name framestats
要了解有关如何使用gfxinfo
和framestats
将UI性能测量集成到测试实践中的更多信息,请参阅编写宏基准测试。
检查网络诊断
指定netstats
服务可提供自上次设备启动以来收集的网络使用情况统计信息。要输出其他信息,例如详细的唯一用户ID(UID)信息,请包含detail
选项,如下所示
adb shell dumpsys netstats detail
输出因连接设备上运行的 Android 版本而异。以下部分介绍了您通常会看到的信息类型。
活动接口和活动UID接口
以下示例输出列出了连接设备的活动接口和活动UID接口。在大多数情况下,活动接口和活动UID接口的信息是相同的。
Active interfaces: iface=wlan0 ident=[{type=WIFI, subType=COMBINED, networkId="Guest"}] Active UID interfaces: iface=wlan0 ident=[{type=WIFI, subType=COMBINED, networkId="Guest"}]
“Dev”和“Xt”统计信息
以下是Dev统计信息部分的示例输出
Dev stats: Pending bytes: 1798112 History since boot: ident=[{type=WIFI, subType=COMBINED, networkId="Guest", metered=false}] uid=-1 set=ALL tag=0x0 NetworkStatsHistory: bucketDuration=3600 st=1497891600 rb=1220280 rp=1573 tb=309870 tp=1271 op=0 st=1497895200 rb=29733 rp=145 tb=85354 tp=185 op=0 st=1497898800 rb=46784 rp=162 tb=42531 tp=192 op=0 st=1497902400 rb=27570 rp=111 tb=35990 tp=121 op=0 Xt stats: Pending bytes: 1771782 History since boot: ident=[{type=WIFI, subType=COMBINED, networkId="Guest", metered=false}] uid=-1 set=ALL tag=0x0 NetworkStatsHistory: bucketDuration=3600 st=1497891600 rb=1219598 rp=1557 tb=291628 tp=1255 op=0 st=1497895200 rb=29623 rp=142 tb=82699 tp=182 op=0 st=1497898800 rb=46684 rp=160 tb=39756 tp=191 op=0 st=1497902400 rb=27528 rp=110 tb=34266 tp=120 op=0
UID统计信息
以下是每个UID的详细统计信息的示例
UID stats: Pending bytes: 744 Complete history: ident=[[type=MOBILE_SUPL, subType=COMBINED, subscriberId=311111...], [type=MOBILE, subType=COMBINED, subscriberId=311111...]] uid=10007 set=DEFAULT tag=0x0 NetworkStatsHistory: bucketDuration=7200000 bucketStart=1406167200000 activeTime=7200000 rxBytes=4666 rxPackets=7 txBytes=1597 txPackets=10 operations=0 ident=[[type=WIFI, subType=COMBINED, networkId="MySSID"]] uid=10007 set=DEFAULT tag=0x0 NetworkStatsHistory: bucketDuration=7200000 bucketStart=1406138400000 activeTime=7200000 rxBytes=17086802 rxPackets=15387 txBytes=1214969 txPackets=8036 operations=28 bucketStart=1406145600000 activeTime=7200000 rxBytes=2396424 rxPackets=2946 txBytes=464372 txPackets=2609 operations=70 bucketStart=1406152800000 activeTime=7200000 rxBytes=200907 rxPackets=606 txBytes=187418 txPackets=739 operations=0 bucketStart=1406160000000 activeTime=7200000 rxBytes=826017 rxPackets=1126 txBytes=267342 txPackets=1175 operations=35
要查找应用的UID,请运行以下命令:adb shell dumpsys package your-package-name
。然后查找标有userId
的行。
例如,要查找应用“com.example.myapp”的网络使用情况,请运行以下命令
adb shell dumpsys package com.example.myapp | grep userId
输出应类似于以下内容
userId=10007 gids=[3003, 1028, 1015]
使用前面的示例转储,查找包含uid=10007
的行。存在两行这样的行——第一行指示移动连接,第二行指示Wi-Fi连接。在每行下方,您可以看到以下每个两小时窗口的信息,bucketDuration
以毫秒为单位指定
-
set=DEFAULT
表示前台网络使用情况,而set=BACKGROUND
表示后台使用情况。set=ALL
表示两者。 -
tag=0x0
指示与流量关联的套接字标记。 -
rxBytes
和rxPackets
分别表示在相应时间间隔内接收到的字节和接收到的数据包。 -
txBytes
和txPackets
分别表示在相应时间间隔内发送(传输)的字节和发送的数据包。
检查电池诊断
指定batterystats
服务会生成有关设备上电池使用情况的统计数据,这些数据按唯一用户ID(UID)组织。要了解如何使用dumpsys
测试应用的Doze和应用待机状态,请参阅使用Doze和应用待机状态进行测试。
用于batterystats
的命令如下所示
adb shell dumpsys batterystats options
要查看可用于batterystats
的其他选项列表,请包含-h
选项。以下示例输出自设备上次充电以来指定应用包的电池使用情况统计信息
adb shell dumpsys batterystats --charged package-name
输出通常包括以下内容
- 电池相关事件的历史记录
- 设备的全局统计信息
- 每个UID和系统组件的大致功耗
- 每个应用的每数据包移动毫秒数
- 系统UID汇总统计信息
- 应用UID汇总统计信息
要了解有关如何使用batterystats
以及生成输出的HTML可视化效果(这使得更容易理解和诊断与电池相关的问题)的更多信息,请阅读使用Batterystats和Battery Historian分析电池使用情况。
检查机器可读输出
您可以使用以下命令以机器可读的CSV格式生成batterystats
输出
adb shell dumpsys batterystats --checkin
以下是一个输出示例
9,0,i,vers,11,116,K,L 9,0,i,uid,1000,android 9,0,i,uid,1000,com.android.providers.settings 9,0,i,uid,1000,com.android.inputdevices 9,0,i,uid,1000,com.android.server.telecom ... 9,0,i,dsd,1820451,97,s-,p- 9,0,i,dsd,3517481,98,s-,p- 9,0,l,bt,0,8548446,1000983,8566645,1019182,1418672206045,8541652,994188 9,0,l,gn,0,0,666932,495312,0,0,2104,1444 9,0,l,m,6794,0,8548446,8548446,0,0,0,666932,495312,0,697728,0,0,0,5797,0,0 ...
电池使用情况观察可能针对UID或系统级。根据数据在分析电池性能中的有用性选择数据以供包含。每一行代表一个观察结果,包含以下元素
- 占位符整数
- 与观察结果关联的用户ID
- 聚合模式
i
表示与充电/未充电状态无关的信息。l
表示--charged
(自上次充电以来的使用情况)。u
表示--unplugged
(自上次拔下电源以来的使用情况)。在Android 5.1.1中已弃用。
- 节标识符,用于确定如何解释该行中的后续值。
下表描述了您可能看到的各种节标识符
节标识符 | 描述 | 剩余字段 |
---|---|---|
|
版本 |
|
|
UID |
|
|
APK |
|
|
进程 |
|
|
传感器 |
|
|
振动器 |
|
|
前台 |
|
|
状态时间 |
|
|
唤醒锁 |
|
|
同步 |
|
|
作业 |
|
|
内核唤醒锁 |
|
|
唤醒原因 |
|
|
网络 |
|
|
用户活动 |
|
|
电池 |
|
|
电池放电 |
|
|
电池电量 |
|
|
Wi-Fi |
|
|
全局Wi-Fi |
|
|
全局蓝牙 |
|
|
其他 |
|
|
全局网络 |
|
|
屏幕亮度 |
|
|
信号扫描时间 |
|
|
信号强度时间 |
|
|
信号强度计数 |
|
|
数据连接时间 |
|
|
数据连接计数 |
无,GPRS,EDGE,UMTS,CDMA,EVDO_0,EVDO_A,1xRTT,HSDPA,HSUPA,HSPA,IDEN,EVDO_B,LTE,EHRPD,HSPAP,其他 |
|
Wi-Fi 状态时间 |
关闭,关闭扫描,开启无网络,开启断开连接,开启连接 STA,开启连接 P2P,开启连接 STA P2P,软 AP |
|
Wi-Fi 状态计数 |
关闭,关闭扫描,开启无网络,开启断开连接,开启连接 STA,开启连接 P2P,开启连接 STA P2P,软 AP |
|
Wi-Fi 认证状态时间 |
无效,断开连接,接口禁用,非活动状态,扫描,正在认证,正在关联,已关联,四向握手,组握手,已完成,休眠,未初始化 |
|
Wi-Fi 认证状态计数 |
无效,断开连接,接口禁用,非活动状态,扫描,正在认证,正在关联,已关联,四向握手,组握手,已完成,休眠,未初始化 |
|
Wi-Fi 信号强度时间 |
|
|
Wi-Fi 信号强度计数 |
|
|
蓝牙状态时间 |
非活动状态,低,中,高 |
|
蓝牙状态计数 |
非活动状态,低,中,高 |
|
功耗汇总 |
电池容量,计算出的功耗,最低消耗功率,最高消耗功率 |
|
功耗项目 |
标签,毫安时 |
|
放电步骤 |
持续时间,电量等级,屏幕,省电模式 |
|
充电步骤 |
持续时间,电量等级,屏幕,省电模式 |
|
剩余放电时间 |
|
|
剩余充电时间 |
|
注意:在 Android 6.0 之前,蓝牙无线电、蜂窝无线电和 Wi-Fi 的功耗在 m
(其他)部分类别中跟踪。在 Android 6.0 及更高版本中,这些组件的功耗在 pwi
(功耗项目)部分中跟踪,每个组件都有单独的标签(wifi
、blue
、cell
)。
查看内存分配
您可以通过两种方式之一检查应用程序的内存使用情况:使用 procstats
在一段时间内进行检查,或者使用 meminfo
在特定时间点进行检查。以下部分将向您展示如何使用这两种方法。
procstats
procstats
使您能够查看应用程序随时间的行为,包括它在后台运行的时间以及在此期间使用的内存量。它可以帮助您快速找到应用程序中的低效率和错误行为(例如内存泄漏),这些行为会影响应用程序的性能,尤其是在低内存设备上运行时。其状态转储显示有关每个应用程序运行时、比例集大小 (PSS)、唯一集大小 (USS) 和驻留集大小 (RSS) 的统计信息。
要获取过去三小时的应用程序内存使用情况统计信息(以人类可读的格式),请运行以下命令
adb shell dumpsys procstats --hours 3
如以下示例所示,输出显示应用程序运行时间的百分比以及 PSS、USS 和 RSS,以 minPSS-avgPSS-maxPSS/minUSS-avgUSS-maxUSS/minRSS-avgRSS-maxRSS
的格式表示,超过样本数。
AGGREGATED OVER LAST 3 HOURS: * com.android.systemui / u0a37 / v28: TOTAL: 100% (15MB-16MB-17MB/7.7MB-8.7MB-9.4MB/7.7MB-9.6MB-84MB over 178) Persistent: 100% (15MB-16MB-17MB/7.7MB-8.7MB-9.4MB/7.7MB-9.6MB-84MB over 178) * com.android.se / 1068 / v28: TOTAL: 100% (2.8MB-2.9MB-2.9MB/300KB-301KB-304KB/304KB-22MB-33MB over 3) Persistent: 100% (2.8MB-2.9MB-2.9MB/300KB-301KB-304KB/304KB-22MB-33MB over 3) * com.google.android.gms.persistent / u0a7 / v19056073: TOTAL: 100% (37MB-38MB-40MB/27MB-28MB-29MB/124MB-125MB-126MB over 2) Imp Fg: 100% (37MB-38MB-40MB/27MB-28MB-29MB/124MB-125MB-126MB over 2) ... * com.android.gallery3d / u0a62 / v40030: TOTAL: 0.01% Receiver: 0.01% (Cached): 54% (6.4MB-6.5MB-6.9MB/4.4MB-4.4MB-4.4MB/4.4MB-26MB-68MB over 6) * com.google.android.tvlauncher / u0a30 / v1010900130: TOTAL: 0.01% Receiver: 0.01% (Cached): 91% (5.8MB-13MB-14MB/3.5MB-10MB-12MB/12MB-33MB-78MB over 6) * com.android.vending:instant_app_installer / u0a16 / v81633968: TOTAL: 0.01% Receiver: 0.01% (Cached): 100% (14MB-15MB-16MB/3.8MB-4.2MB-5.1MB/3.8MB-30MB-95MB over 7) ... Run time Stats: SOff/Norm: +32m52s226ms SOn /Norm: +2h10m8s364ms Mod : +17s930ms TOTAL: +2h43m18s520ms Memory usage: Kernel : 265MB (38 samples) Native : 73MB (38 samples) Persist: 262MB (90 samples) Top : 190MB (325 samples) ImpFg : 204MB (569 samples) ImpBg : 754KB (345 samples) Service: 93MB (1912 samples) Receivr: 227KB (1169 samples) Home : 66MB (12 samples) LastAct: 30MB (255 samples) CchAct : 220MB (450 samples) CchCAct: 193MB (71 samples) CchEmty: 182MB (652 samples) Cached : 58MB (38 samples) Free : 60MB (38 samples) TOTAL : 1.9GB ServRst: 50KB (278 samples) Start time: 2015-04-08 13:44:18 Total elapsed time: +2h43m18s521ms (partial) libart.so
meminfo
您可以使用以下命令记录应用程序内存如何在不同类型的 RAM 分配之间分配的快照
adb shell dumpsys meminfo package_name|pid [-d]
-d
标志打印与 Dalvik 和 ART 内存使用情况相关的更多信息。
输出列出应用程序当前的所有分配,以千字节为单位。
检查此信息时,您应该熟悉以下类型的分配
- 私有(干净和脏)RAM
- 这是仅由您的进程使用的内存。这是系统在销毁应用程序进程时可以回收的大部分 RAM。通常,其中最重要的部分是私有脏 RAM,它是最昂贵的,因为它仅由您的进程使用,并且其内容仅存在于 RAM 中,因此无法分页到存储,因为 Android 不使用交换。您进行的所有 Dalvik 和本机堆分配都是私有脏 RAM。您与 Zygote 进程共享的 Dalvik 和本机分配是共享脏 RAM。
- 比例集大小 (PSS)
- 这是应用程序 RAM 使用量的度量,它考虑了跨进程共享页面。任何对您的进程唯一的 RAM 页面都会直接影响其 PSS 值,而与其他进程共享的页面只会按共享量成比例地影响 PSS 值。例如,两个进程之间共享的页面将其大小的一半贡献给每个进程的 PSS。
PSS 度量的一个特征是,您可以将所有进程的 PSS 相加以确定所有进程实际使用的内存。这意味着 PSS 对于进程的实际 RAM 权重以及与其他进程的 RAM 使用量和可用总 RAM 的比较是一个很好的度量。
例如,以下是 Nexus 5 设备上 Map 进程的输出
adb shell dumpsys meminfo com.google.android.apps.maps -d
注意:您看到的信息可能与此处显示的信息略有不同,因为输出的一些详细信息在不同平台版本之间有所不同。
** MEMINFO in pid 18227 [com.google.android.apps.maps] ** Pss Private Private Swapped Heap Heap Heap Total Dirty Clean Dirty Size Alloc Free ------ ------ ------ ------ ------ ------ ------ Native Heap 10468 10408 0 0 20480 14462 6017 Dalvik Heap 34340 33816 0 0 62436 53883 8553 Dalvik Other 972 972 0 0 Stack 1144 1144 0 0 Gfx dev 35300 35300 0 0 Other dev 5 0 4 0 .so mmap 1943 504 188 0 .apk mmap 598 0 136 0 .ttf mmap 134 0 68 0 .dex mmap 3908 0 3904 0 .oat mmap 1344 0 56 0 .art mmap 2037 1784 28 0 Other mmap 30 4 0 0 EGL mtrack 73072 73072 0 0 GL mtrack 51044 51044 0 0 Unknown 185 184 0 0 TOTAL 216524 208232 4384 0 82916 68345 14570 Dalvik Details .Heap 6568 6568 0 0 .LOS 24771 24404 0 0 .GC 500 500 0 0 .JITCache 428 428 0 0 .Zygote 1093 936 0 0 .NonMoving 1908 1908 0 0 .IndirectRef 44 44 0 0 Objects Views: 90 ViewRootImpl: 1 AppContexts: 4 Activities: 1 Assets: 2 AssetManagers: 2 Local Binders: 21 Proxy Binders: 28 Parcel memory: 18 Parcel count: 74 Death Recipients: 2 OpenSSL Sockets: 2
这是 Gmail 应用程序在 Dalvik 上的旧版 dumpsys
** MEMINFO in pid 9953 [com.google.android.gm] ** Pss Pss Shared Private Shared Private Heap Heap Heap Total Clean Dirty Dirty Clean Clean Size Alloc Free ------ ------ ------ ------ ------ ------ ------ ------ ------ Native Heap 0 0 0 0 0 0 7800 7637(6) 126 Dalvik Heap 5110(3) 0 4136 4988(3) 0 0 9168 8958(6) 210 Dalvik Other 2850 0 2684 2772 0 0 Stack 36 0 8 36 0 0 Cursor 136 0 0 136 0 0 Ashmem 12 0 28 0 0 0 Other dev 380 0 24 376 0 4 .so mmap 5443(5) 1996 2584 2664(5) 5788 1996(5) .apk mmap 235 32 0 0 1252 32 .ttf mmap 36 12 0 0 88 12 .dex mmap 3019(5) 2148 0 0 8936 2148(5) Other mmap 107 0 8 8 324 68 Unknown 6994(4) 0 252 6992(4) 0 0 TOTAL 24358(1) 4188 9724 17972(2)16388 4260(2)16968 16595 336 Objects Views: 426 ViewRootImpl: 3(8) AppContexts: 6(7) Activities: 2(7) Assets: 2 AssetManagers: 2 Local Binders: 64 Proxy Binders: 34 Death Recipients: 0 OpenSSL Sockets: 1 SQL MEMORY_USED: 1739 PAGECACHE_OVERFLOW: 1164 MALLOC_SIZE: 62
通常,只关注 Pss Total
和 Private Dirty
列。在某些情况下,Private Clean
和 Heap Alloc
列也会提供有趣的数据。
以下提供了有关您应该观察的不同内存分配的更多信息
Dalvik 堆
- 应用程序中 Dalvik 分配使用的 RAM。
Pss Total
包括所有 Zygote 分配(按其跨进程共享加权),如 PSS 定义中所述。Private Dirty
数字是实际提交给应用程序堆的 RAM,由您自己的分配以及自从从 Zygote 分叉应用程序进程以来已修改的任何 Zygote 分配页面组成。注意:在具有
Dalvik Other
部分的新平台版本上,Dalvik 堆的Pss Total
和Private Dirty
数字不包括 Dalvik 开销,例如即时编译 (JIT) 和 GC 簿记,而旧版本将所有这些都组合在Dalvik
下列出。Heap Alloc
是 Dalvik 和本机堆分配器跟踪的应用程序内存量。此值大于Pss Total
和Private Dirty
,因为您的进程是从 Zygote 分叉的,并且包含您的进程与所有其他进程共享的分配。 .so mmap
和.dex mmap
- 用于映射
.so
(本机)和.dex
(Dalvik 或 ART)代码的 RAM。Pss Total
数字包括跨应用程序共享的平台代码。Private Clean
是应用程序自己的代码。通常,实际映射的大小更大。此处的 RAM 仅是应用程序已执行的代码当前需要驻留在 RAM 中的部分。但是,.so mmap
具有较大的私有脏,这是由于在将本机代码加载到其最终地址时对其进行修复所致。 .oat mmap
- 这是代码映像使用的 RAM 量。它基于多个应用程序常用的预加载类。此映像在所有应用程序之间共享,不受特定应用程序的影响。
.art mmap
- 这是堆映像使用的 RAM 量。它基于多个应用程序常用的预加载类。此映像在所有应用程序之间共享,不受特定应用程序的影响。即使 ART 映像包含
Object
实例,它也不计入您的堆大小。 .Heap
(仅使用-d
标志)- 这是应用程序的堆内存量。这排除映像和大型对象空间中的对象,但包括 Zygote 空间和非移动空间。
.LOS
(仅使用-d
标志)- 这是 ART 大型对象空间使用的 RAM 量。这包括 Zygote 大型对象。大型对象是所有大于 12KB 的原始数组分配。
.GC
(仅使用-d
标志)- 这是垃圾回收的开销成本。无法减少此开销。
.JITCache
(仅使用-d
标志)- 这是 JIT 数据和代码缓存使用的内存量。通常,此值为零,因为所有应用程序都在安装时编译。
.Zygote
(仅使用-d
标志)- 这是 Zygote 空间使用的内存量。Zygote 空间在设备启动期间创建,并且从未分配。
.NonMoving
(仅使用-d
标志)- 这是 ART 非移动空间使用的 RAM 量。非移动空间包含特殊的不可移动对象,例如字段和方法。您可以通过在应用程序中使用更少的字段和方法来减少此部分。
.IndirectRef
(仅使用-d
标志)- 这是 ART 间接引用表使用的 RAM 量。通常此数量很小,但如果过高,您可以通过减少使用的本地和全局 JNI 引用数量来减少它。
未知
- 系统无法将其分类到其他更具体的项目中的任何 RAM 页面。当前,这主要包含本机分配,由于地址空间布局随机化 (ASLR),工具在收集此数据时无法识别这些分配。与 Dalvik 堆一样,
Unknown
的Pss Total
考虑了与 Zygote 的共享,Private Dirty
是仅专用于应用程序的未知 RAM。 总计
- 进程使用的比例集大小 (PSS) RAM 的总和。这是其上方所有 PSS 字段的总和。它指示进程的整体内存权重,可以直接与其他进程和可用总 RAM 进行比较。
Private Dirty
和Private Clean
是进程内的总分配,这些分配与其他进程不共享。当进程被销毁时,来自这些分配的所有 RAM 都将释放回系统。Private Clean
也可以在进程被销毁之前被换出并释放,但Private Dirty
仅在进程销毁时释放。脏内存(Dirty RAM)是指已被修改的页面,由于没有交换空间,因此必须驻留在内存中。干净内存(Clean RAM)是指从持久性文件映射的页面,例如正在执行的代码,如果一段时间内未使用,则可以将其换出。
ViewRootImpl
- 进程中处于活动状态的根视图数量。每个根视图都与一个窗口关联,因此这可以帮助您识别涉及对话框或其他窗口的内存泄漏。
AppContexts
和Activities
- 当前存在于您的进程中的应用程序
Context
和Activity
对象的数量。这可以帮助您快速识别由于静态引用而无法被垃圾回收的泄漏的Activity
对象,这很常见。这些对象通常与许多其他分配相关联,这使得它们成为跟踪大型内存泄漏的好方法。