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 时的可用选项:
表 1. 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 版本不同而异。以下部分介绍您通常会看到的信息类型。
Event Hub 状态
以下是您在检查输入诊断信息的 Event Hub 状态时可能会看到的信息示例:
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
...
  Input Reader 状态
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
在 Input Reader 状态转储的结尾部分,会显示一些关于全局配置参数的信息,例如点按时间间隔:
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
Input Dispatcher 状态
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 服务的输出时要考虑的事项:
Event Hub 状态:
- 所有预期的输入设备是否都存在。
- 每个输入设备是否都有适当的按键布局文件、按键字符映射文件和输入设备配置文件。如果这些文件缺失或包含语法错误,则无法加载它们。
-  每个输入设备是否都已正确分类。Classes字段中的位是否对应于EventHub.h中的标志,如INPUT_DEVICE_CLASS_TOUCH_MT。
-  BuiltInKeyboardId是否正确无误。如果设备未配备内置键盘,则该 ID 必须为-2。否则,应为内置键盘的 ID。
- 如果您发现 BuiltInKeyboardId应该为-2,但却不是,则说明缺少某个特殊功能小键盘的按键字符映射文件。特殊功能小键盘设备应具有仅包含type SPECIAL_FUNCTION行的按键字符映射文件。
Input Reader 状态:
- 所有的预期输入设备是否都存在。
- 每个输入设备是否都已配置正确。特别注意检查触摸屏和操纵杆轴是否正确。
Input Dispatcher 状态:
- 所有输入事件是否均按预期进行处理。
- 轻触触摸屏的同时运行 dumpsys后,TouchStates行是否正确标识了您所轻触的窗口。
测试界面性能
如果指定 gfxinfo 服务,输出中会包含录制阶段所发生的动画帧的相关性能信息。
以下命令使用 gfxinfo 收集指定软件包名称的界面性能数据:
adb shell dumpsys gfxinfo package-name
您还可以包含 framestats 选项,以提供有关最近发生的帧的更加详细的帧时间信息,让您能够更准确地找到问题并进行调试:
adb shell dumpsys gfxinfo package-name framestats
如需详细了解如何使用 gfxinfo 和 framestats 将界面性能测量值集成到测试实践中,请参阅编写 Macrobenchmark。
检查网络诊断信息
指定 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 在低电耗模式和应用待机模式下测试应用,请参阅在低电耗模式和应用待机模式下进行测试。
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 中已废弃。
 
- 区段标识符,用于确定如何解译行中的后续值。
下表介绍了您可能会看到的各种区段标识符:
表 2. 区段标识符列表
| 区段标识符 | 说明 | 其余字段 | 
|---|---|---|
| 
 | 版本 | 
 | 
| 
 | UID | 
 | 
| 
 | APK | 
 | 
| 
 | 进程 | 
 | 
| 
 | 传感器 | 
 | 
| 
 | 振动器 | 
 | 
| 
 | 前景 | 
 | 
| 
 | 状态时间 | 
 | 
| 
 | 唤醒锁 | 
 | 
| 
 | 同步 | 
 | 
| 
 | 作业 | 
 | 
| 
 | 内核唤醒锁定 | 
 | 
| 
 | 唤醒原因 | 
 | 
| 
 | 网络 | 
 | 
| 
 | 用户活动 | 
 | 
| 
 | 电池 | 
 | 
| 
 | 电池电量消耗 | 
 | 
| 
 | 电量 | 
 | 
| 
 | Wi-Fi | 
 | 
| 
 | 全局 Wi-Fi | 
 | 
| 
 | 全局蓝牙 | 
 | 
| 
 | 其他 | 
 | 
| 
 | 全局网络 | 
 | 
| 
 | 屏幕亮度 | 
 | 
| 
 | 信号扫描时间 | 
 | 
| 
 | 信号强度时间 | 
 | 
| 
 | 信号强度计数 | 
 | 
| 
 | 数据连接时间 | 
 | 
| 
 | 数据连接计数 | 
 | 
| 
 | Wi-Fi 状态时间 | 
 | 
| 
 | WLAN 状态计数 | 
 | 
| 
 | 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 [-d] package_name|pid
-d 标记会输出更多与 Dalvik 和 ART 内存占用情况相关的信息。-h 标志会输出所有支持的标志。
输出列出了应用当前的所有分配,以千字节为单位。
检查此信息时,您应熟悉以下分配类型:
- 私有(干净和脏)RAM
- 这是仅由您的进程使用的内存。这是您的应用进程销毁后系统可以回收的 RAM 容量。 通常情况下,最重要的部分是私有脏 RAM,它的开销最大,因为只有您的进程使用它,而且其内容仅存在于 RAM 中,所以无法通过分页机制映射到硬盘(因为 Android 不使用交换)。您进行的所有 Dalvik 和原生堆分配都将是私有脏 RAM。您与 Zygote 进程共享的 Dalvik 和原生分配则是共享脏 RAM。
- 实际使用的物理内存(比例分配共享库占用的内存,PSS)
- 这是对应用 RAM 占用情况的衡量,考虑了在进程之间共享 RAM 页的情况。您的进程独占的 RAM 页会直接计入其 PSS 值,而与其他进程共享的 RAM 页则仅会按相应比例计入 PSS 值。例如,两个进程共享的页面会占每个进程的 PSS 的一半。
PSS 衡量的一个特点是,您可以将所有进程的 PSS 加起来确定所有进程占用的实际内存。这表示 PSS 是一种理想的方式,可用来衡量进程的实际 RAM 占用比重,以及相对于其他进程和可用的总 RAM 而言,对 RAM 的占用情况。
例如,下面是 Nexus 5 设备上的地图进程的输出:
adb shell dumpsys meminfo -d com.google.android.apps.maps
注意:您看到的信息可能会与此处显示的内容稍有不同,因为输出中的某些详细信息在不同平台版本上会有所不同。
** 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 Heap
- 您应用中的 Dalvik 分配所占用的 RAM。Pss Total包括所有 Zygote 分配(如 PSS 定义中所述,通过进程之间共享的内存容量来衡量)。Private Dirty值是仅分配给您的应用堆的实际 RAM,包含了您自己的分配和任何 Zygote 分配页,这些分配页自从 Zygote 派生您的应用进程以来已被修改。注意:在包含 Dalvik Other区段的更高平台版本上,Dalvik 堆的Pss Total和Private Dirty数值不包括 Dalvik 开销(例如即时编译 (JIT) 和垃圾回收记录),而更低的版本会在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具有较大容量的私有脏 RAM,这是因为在将其加载到最终地址时对原生代码进行了修复。
- .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 引用数量来减小此容量。
- Unknown
- 系统无法将其分类到其他更具体的一个项中的任何 RAM 页。当前,此类 RAM 页主要包含原生分配,由于地址空间布局随机化 (ASLR),工具在收集此数据时无法识别这些分配。与 Dalvik 堆相同,Unknown的Pss Total考虑了与 Zygote 共享的容量,且Private Dirty是仅由您的应用占用的未知 RAM。
- TOTAL
- 您的进程占用的按比例分摊的内存大小 (PSS) RAM 总容量,等于上述所有 PSS 字段的总和。该值表示了您的进程占用的内存容量占总体内存容量的比重,可以直接与其他进程和可用的总 RAM 进行比较。
Private Dirty和Private Clean合起来就是您进程中的总分配,这些分配未与其他进程共享。 当您的进程被销毁时,这些分配中的所有 RAM 都会释放回系统。Private Clean也可以在进程销毁之前被换出和释放,但Private Dirty仅在进程销毁后被释放。脏 RAM 页由于已被修改过,因此必须保留在 RAM 中(因为没有交换)。干净 RAM 页是从某个持久性文件(例如正在执行的代码)映射而来的,因此如果暂时不使用,可以将其换出 RAM。 
- ViewRootImpl
- 您的进程中当前处于活动状态的根视图数量。每个根视图都与一个窗口关联,因此该值有助于您确定与对话框或其他窗口有关的内存泄漏。
- AppContexts和- Activities
- 您的进程中当前处于活动状态的应用 Context和Activity对象数量。该值可以帮助您快速确定发生泄漏的Activity对象,这些对象由于存在对其的静态引用(比较常见)而无法进行垃圾回收。这些对象往往关联了许多其他分配,因此是查找大型内存泄漏的理想工具。
