原创文章,转载请注明出处,多谢!

如果cpu频率、调度 和 compiler filter都一一排除了,没问题。那接下来就看是否有具体方法耗时。

一、常用的分析手段:

1.systrace

这里可按systrace中各个阶段来逐段对比分析,当然这里也分冷热启。

冷启动可以拆分如下若干阶段:

deliver input / fork process / bind application / activity start / doFrame / drawFrame / SF invalidate&refresh

热启动就主要考虑绘制和渲染了。

看是否差距集中在一个某个阶段内,如果是特定区域的差异那么就来针对具体方法耗时进行分析。

找到有差异的阶段可以通过加trace,来缩小范围和细化具体方法。

各层加trace的方式:

APP:

Trace.beginSection("");

Trace.endSection();

注:抓systrace的时候需要指定对应的app进程。

系统java层:

Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate”);

Trace.traceEnd(Trace.TRACE_TAG_VIEW);

注:前面的tag参数对应的抓systrace的开关选项。

Native:

#define ATRACE_TAG ATRACE_TAG_ALWAYS

#include // for c++

#include // for c

ATRACE_CALL();

or

ATRACE_BEGIN("");

ATRACE_END();

2. TraceView or AS profile cpu

对于app的问题,可以借助traceView 或者 as profile cpu检查 CPU Activity 和函数跟踪来帮助定位耗时方法,不断缩小范围来定位问题。

3. 反编译

对于三方app,没有源码,可以通过oatdump反编译来分析:

adb shell oatdump --oat-file=/data/app/包名/oat/arm64/base.odex > demo.txt

字节码命令说明

二、实战举例:高德地图耗时分析

1.发现问题

在cpu频率、调度 和 compiler filter都一一排除了的前提下,通过systrace来分析具体启动各阶段耗时情况。

Android N:

Android O:

发现MapContainer执行时间比较长,MapContainer 是高德自己实现的类,应该是高德自己的实现方式在不同 Android 版本上差别比较大。因为高德是三方应用,没有代码,所以借助traceView来分析。

注:如果是冷启动可以使用命令行来抓取:

1)启动指定 Activity 同时启动 trace

am start -n com.stan.note.newdemo/.MainActivity --start-profiler /data/local/tmp/test.trace

am profile com.stan.note.newdemo stop

2)启动指定 Activity 同时启动 trace, 自动结束

am start -n com.stan.note.newdemo/.MainActivity -P /data/local/tmp/test.trace

通过 TraceView 发现有两个相关的方法非常耗时:

com.autonavi.mao.core.OverlayManager.init ()V

com.autonavi.minimap.commute.CommuteOverlay.init ()V

下面通过oatdump来反编译这两个方法

截取一个小片段:

void com.autonavi.minimap.commute.CommuteOverlay.init() (dex_method_idx=20281)

DEX CODE:

0x0000: 1202 | const/4 v2, #+0

0x0001: e530 0800 | iget-object-quick v0, v3, // offset@8

0x0003: 7110 6006 0000 | invoke-static {v0}, android.view.LayoutInflater android.view.LayoutInflater.from(android.content.Context) // method@1632

0x0006: 0c00 | move-result-object v0

0x0007: 6001 a235 | sget v1, I com.autonavi.minimap.R$layout.commute_marker_layout // field@13730

0x0009: e930 1200 1002 | invoke-virtual-quick {v0, v1, v2}, // vtable@18

0x000c: 0c00 | move-result-object v0

这里就想找到art指令中对应的函数并加上trace,来确定是哪个具体函数耗时。

比如sget指令,对应到如下解释器解释指令

art/runtime/interpreter/mterp/arm64/op_sget.S

%default { "is_object":"0", "helper":"MterpGet32Static", "extend":"" }

2 /*

3 * General SGET handler wrapper.

4 *

5 * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short

6 */

7 /* op vAA, field//BBBB */

8

9 .extern $helper

10 EXPORT_PC

11 FETCH w0, 1 // w0

12 ldr x1, [xFP, #OFF_FP_METHOD]

13 mov x2, xSELF

14 bl $helper

15 ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]

16 lsr w2, wINST, #8 // w2

17 $extend

18 PREFETCH_INST 2

19 cbnz x3, MterpException // bail out

20.if $is_object

21 SET_VREG_OBJECT w0, w2 // fp[AA]

22.else

23 SET_VREG w0, w2 // fp[AA]

24.endif

25 ADVANCE 2

26 GET_INST_OPCODE ip // extract opcode from rINST

27 GOTO_OPCODE ip

这部分是汇编指令,具体指令执行不耗时,肯定是函数耗时。函数调用$helper 对应MterpGet32Static函数。

在对应的函数处加trace

art/runtime/interpreter/mterp/mterp.cc

#include "base/systrace.h"

extern "C" int MterpSet32Static(uint32_t field_idx,

int32_t new_value,

ArtMethod* referrer,

Thread* self)

REQUIRES_SHARED(Locks::mutator_lock_) {

return MterpSetStatic(field_idx,

new_value,

referrer,

self,

&ArtField::SetInt);

}

在对应MterpSetStatic方法加trace.

template

ALWAYS_INLINE return_type MterpGetStatic(uint32_t field_idx,

ArtMethod* referrer,

Thread* self,

return_type (ArtField::*func)(ObjPtr<:object>))

REQUIRES_SHARED(Locks::mutator_lock_) {

ScopedTrace trace(__FUNCTION__);

return_type res = 0; // On exception, the result will be ignored.

ArtField* f =

FindFieldFromCode(field_idx,

referrer,

self,

primitive_type);

if (LIKELY(f != nullptr)) {

ObjPtr<:object> obj = f->GetDeclaringClass();

res = (f->*func)(obj);

}

return res;

}

2.分析问题

MterpGetStatic 就是去获取一个类的静态成员, 为什么会用掉 85 ms ?apk中dex文件会在art中转化为一个DexFile对象,而每一个 DexFile 对象会对应一个 DexCache 对象。DexCache 的作用是用来缓存包含在一个 dex 文件里面的类型 (Type), 方法 (Method), 域 (Field), 字符串 (String) 和静态储存区 (Static Storage) 等信息。

art/runtime/mirror/dex_cache.cc

void DexCache::Init(const DexFile* dex_file,

ObjPtr location,

StringDexCacheType* strings,

uint32_t num_strings,

TypeDexCacheType* resolved_types,

uint32_t num_resolved_types,

MethodDexCacheType* resolved_methods,

uint32_t num_resolved_methods,

FieldDexCacheType* resolved_fields,

uint32_t num_resolved_fields,

MethodTypeDexCacheType* resolved_method_types,

uint32_t num_resolved_method_types,

GcRoot* resolved_call_sites,

uint32_t num_resolved_call_sites)

REQUIRES_SHARED(Locks::mutator_lock_);

上面是 DexCache 的初始化函数, num_resolved_fields 表示 DexCache 中缓存 Field 的个数, 来打印一下这个参数

N:

03-21 15:57:56.409 5982 5982 E art : syh DexCache::Init classes.dex num_resolved_fields 56285

03-21 15:57:56.433 5982 5982 E art : syh DexCache::Init classes.dex num_resolved_fields 25449

03-21 15:57:56.437 5982 5982 E art : syh DexCache::Init classes.dex num_resolved_fields 63062

03-21 15:57:56.569 5982 5982 E art : syh DexCache::Init classes.dex num_resolved_fields 7802

03-21 15:57:56.917 5982 6097 E art : syh DexCache::Init classes.dex num_resolved_fields 115

03-21 15:57:58.088 5982 6032 E art : syh DexCache::Init classes.dex num_resolved_fields 16

03-21 15:57:58.993 5982 6121 E art : syh DexCache::Init classes.dex num_resolved_fields 27

O:

03-17 14:31:41.834 5358 5358 E zygote : syh DexCache::Init classes.dex num_resolved_fields 1024

03-17 14:31:41.854 5358 5358 E zygote : syh DexCache::Init classes.dex num_resolved_fields 1024

03-17 14:31:41.860 5358 5358 E zygote : syh DexCache::Init classes.dex num_resolved_fields 1024

03-17 14:31:42.051 5358 5358 E zygote : syh DexCache::Init classes.dex num_resolved_fields 1024

03-17 14:31:42.302 5358 5436 E zygote : syh DexCache::Init classes.dex num_resolved_fields 279

03-17 14:31:42.448 5358 5469 E zygote : syh DexCache::Init classes.dex num_resolved_fields 115

03-17 14:31:42.914 5358 5541 E zygote : syh DexCache::Init classes.dex num_resolved_fields 16

03-17 14:31:44.516 5358 5492 E zygote : syh DexCache::Init classes.dex num_resolved_fields 27

Android O 上, 一个 DexCache 最多缓存 1024 个 Field, 而实际上有上万个 Filed, 导致 MterpGetStatic 的时候 cache 命中率极低, 最终导致 MterpGetStatic 耗时。

3.解决问题

尝试调整cache size与N一致,MterpGetStatic 明显改善, 单一个 inflate 就快了 80 ms, 优化后高德地图的启动时间可以减少 166 ms。

android 耗时分析,启动耗时分析(四)-具体方法耗时分析相关推荐

  1. Android studio中NDK开发(四)——使用addr2line分析Crash日志 backtrace

    文章目录 一.前言 二.分析 1.先提取backtrace部分 2.提取对应so库的信息 3.提取错误地址 三.使用addr2line对地址进行转换 1.addr2line工具的路径放在 2.Term ...

  2. android 图片资源目录下,Android编程实现获取图片资源的四种方法

    本文实例讲述了Android编程实现获取图片资源的四种方法.分享给大家供大家参考,具体如下: 1. 图片放在sdcard中: Bitmap imageBitmap = BitmapFactory.de ...

  3. 克制linux启动盘,制作Linux启动盘的四种方法

    出处: Linux的启动软盘有boot盘和boot/root盘之分,所谓的boot盘只能用来启动已经安装在硬盘上的 Linux系统,而boot/root盘本身就是一个迷你Linux系统.Linux启动 ...

  4. linux dd命令制作软盘,制作Linux启动软盘的四种方法

    Linux的启动软盘有boot盘和boot/root盘之分,所谓的boot盘只能用来启动已经安装在硬盘上的Linux系统,而boot/root盘本身就是一个迷你Linux系统.Linux启动软盘是系统 ...

  5. linux系统制作系统盘,制作Linux启动盘的四种方法

    1.使用mkbootdisk工具为当前系统制作启动盘 mkbootdisk --device /dev/fd0 `uname -r` 参数说明:--device /dev/fd0是要建立启动软盘,也是 ...

  6. Android Framework 电源子系统(05)核心方法updatePowerStateLocked分析-3 更新屏保  发送通知  更新wakelock

    该系列文章总纲链接:专题分纲目录 Android Framework 电源子系统 本章关键点总结 & 说明: 本章节主要关注➕ updatePowerStateLocked 方法中 更新屏保 ...

  7. Android Framework 电源子系统(04)核心方法updatePowerStateLocked分析-2 循环处理  更新显示设备状态

    该系列文章总纲链接:专题分纲目录 Android Framework 电源子系统 本章关键点总结 & 说明: 本章节主要关注➕ updatePowerStateLocked 方法中 循环处理 ...

  8. mediasoup 源码分析(二十四)stun 协议分析

    目录 stun介绍 stun 协议分析 属性消息 stun介绍 stun 即:Session Traversal Utilities for NAT.用于NAT穿透的网络协议. 作用: stun协议主 ...

  9. VxWorks 启动程序的四种方法

    文章目录 1.背景介绍 1.1.Vxworks工程 1.2.Vxworks shell 2.内核应用程序(DKM)自启动 2.1.需求来源 2.2.方案1(失败) 2.3.方案2(成功) 3.用户应用 ...

  10. Android开发中保存数据的四种方法方法

    本文来自:安卓航班网 在Android开发中我们会接触到四种数据存储方式,每种存储方式都各有不同:以下安卓航班分别列举了Android开发中的不同存储方式的特点. 一,Preferences Pref ...

最新文章

  1. 近期活动盘点:首期AI Time PhD:清北师兄分享前沿研究成果、“科技驱动,正向创新”医工结合高峰论坛...
  2. C语言实验——打印金字塔_JAVA
  3. 线程同步的各种方法汇总
  4. python将字典导入excel_python将字典列表导出为Excel文件的方法
  5. 背景区域为负样本什么意思_词向量-skipgram与负采样
  6. 动态规划_数字三角形
  7. 【Silverlight】Bing Maps学习系列(三):如何控制地图
  8. cartographer attempt to index global ‘SPARSE_POSE_GRAPH‘ (a nil value)
  9. 大家好,我就是那个提【换手机壳颜色变app主题】需求的“产品经理”
  10. python曲线和直线的交点_求直线与分段线性曲线的交点
  11. 同文输入法 android,同文输入法
  12. 计算机文献检索语言,文献检索语言
  13. 微信公众号编辑模式下推送消息
  14. Java 后台验证码汉字拼音校验
  15. html5转换成mp4,ffmpeg将mov文件转换为mp4的HTML5视频标签IE9
  16. 幼麟棋牌登录socket服务器分析
  17. nodex 解决跨哉请求的解决方案express
  18. 单位可不可以起诉来解除劳动关系
  19. 华为“消费者业务”更名为“终端业务”,正式进军商用终端市场
  20. 按揭贷款买房流程及注意事项详解 你准备好买房了吗

热门文章

  1. 上海市促进文化创意产业发展财政扶持资金项目政策解读
  2. java 只有try 不catch_Java异常处理只有Try-Catch吗?
  3. JAVA毕设项目猎头公司业务管理系统的设计与实现(java+VUE+Mybatis+Maven+Mysql)
  4. linux 使用cp卡死,细说CP使用
  5. 第十九章:如何组建团队
  6. android删除界面工具,Android手机添加/删除桌面图标和插件(图文)
  7. 未来五年移动互联网有哪些热门技术?(转)
  8. 手机收不到第三方发送的短信及验证码
  9. 爬虫:UnicodeDecodeError: 'gbk' codec can't decode byte 0xa6 in position
  10. 无盘疑难杂症的处理和网络的设置(转)