啟動(dòng)耗時(shí)分析(四)-具體方法耗時(shí)分析

原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明出處咙冗,多謝沾歪!

如果cpu頻率、調(diào)度 和 compiler filter都一一排除了雾消,沒(méi)問(wèn)題灾搏。那接下來(lái)就看是否有具體方法耗時(shí)。

一仪或、常用的分析手段:

1.systrace
這里可按systrace中各個(gè)階段來(lái)逐段對(duì)比分析确镊,當(dāng)然這里也分冷熱啟。
冷啟動(dòng)可以拆分如下若干階段:
deliver input / fork process / bind application / activity start / doFrame / drawFrame / SF invalidate&refresh
熱啟動(dòng)就主要考慮繪制和渲染了范删。

看是否差距集中在一個(gè)某個(gè)階段內(nèi)蕾域,如果是特定區(qū)域的差異那么就來(lái)針對(duì)具體方法耗時(shí)進(jìn)行分析。
找到有差異的階段可以通過(guò)加trace,來(lái)縮小范圍和細(xì)化具體方法旨巷。

各層加trace的方式:
APP:

Trace.beginSection("");
Trace.endSection();

注:抓systrace的時(shí)候需要指定對(duì)應(yīng)的app進(jìn)程巨缘。

系統(tǒng)java層:

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

注:前面的tag參數(shù)對(duì)應(yīng)的抓systrace的開(kāi)關(guān)選項(xiàng)。

Native:

#define ATRACE_TAG ATRACE_TAG_ALWAYS

#include <utils/Trace.h> // for c++
#include <cutils/trace.h> // for c

ATRACE_CALL();
or
ATRACE_BEGIN("");
ATRACE_END();

2. TraceView or AS profile cpu
對(duì)于app的問(wèn)題采呐,可以借助traceView 或者 as profile cpu檢查 CPU Activity 和函數(shù)跟蹤來(lái)幫助定位耗時(shí)方法若锁,不斷縮小范圍來(lái)定位問(wèn)題。

3. 反編譯
對(duì)于三方app斧吐,沒(méi)有源碼又固,可以通過(guò)oatdump反編譯來(lái)分析:
adb shell oatdump --oat-file=/data/app/包名/oat/arm64/base.odex > demo.txt
字節(jié)碼命令說(shuō)明

二、實(shí)戰(zhàn)舉例:高德地圖耗時(shí)分析

1.發(fā)現(xiàn)問(wèn)題

在cpu頻率煤率、調(diào)度 和 compiler filter都一一排除了的前提下仰冠,通過(guò)systrace來(lái)分析具體啟動(dòng)各階段耗時(shí)情況。

Android N:

Android O:

發(fā)現(xiàn)MapContainer執(zhí)行時(shí)間比較長(zhǎng)蝶糯,MapContainer 是高德自己實(shí)現(xiàn)的類洋只,應(yīng)該是高德自己的實(shí)現(xiàn)方式在不同 Android 版本上差別比較大。因?yàn)楦叩率侨綉?yīng)用昼捍,沒(méi)有代碼识虚,所以借助traceView來(lái)分析。

注:如果是冷啟動(dòng)可以使用命令行來(lái)抓榷什纭:

1)啟動(dòng)指定 Activity 同時(shí)啟動(dòng) trace

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

am profile com.stan.note.newdemo stop

2)啟動(dòng)指定 Activity 同時(shí)啟動(dòng) trace, 自動(dòng)結(jié)束

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

通過(guò) TraceView 發(fā)現(xiàn)有兩個(gè)相關(guān)的方法非常耗時(shí):

com.autonavi.mao.core.OverlayManager.init ()V
com.autonavi.minimap.commute.CommuteOverlay.init ()V

下面通過(guò)oatdump來(lái)反編譯這兩個(gè)方法

截取一個(gè)小片段:

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指令中對(duì)應(yīng)的函數(shù)并加上trace担锤,來(lái)確定是哪個(gè)具體函數(shù)耗時(shí)。

比如sget指令郊闯,對(duì)應(yīng)到如下解釋器解釋指令

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<- field ref BBBB
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<- AA
17    $extend
18    PREFETCH_INST 2
19    cbnz  x3, MterpException            // bail out
20.if $is_object
21    SET_VREG_OBJECT w0, w2              // fp[AA]<- w0
22.else
23    SET_VREG w0, w2                     // fp[AA]<- w0
24.endif
25    ADVANCE 2
26    GET_INST_OPCODE ip                  // extract opcode from rINST
27    GOTO_OPCODE ip

這部分是匯編指令妻献,具體指令執(zhí)行不耗時(shí)蛛株,肯定是函數(shù)耗時(shí)团赁。函數(shù)調(diào)用$helper 對(duì)應(yīng)MterpGet32Static函數(shù)。

在對(duì)應(yīng)的函數(shù)處加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<int32_t, Primitive::kPrimInt>(field_idx,
                                                      new_value,
                                                      referrer,
                                                      self,
                                                      &ArtField::SetInt<false>);
}

在對(duì)應(yīng)MterpSetStatic方法加trace.

template <typename return_type, Primitive::Type primitive_type>
ALWAYS_INLINE return_type MterpGetStatic(uint32_t field_idx,
                                         ArtMethod* referrer,
                                         Thread* self,
                                         return_type (ArtField::*func)(ObjPtr<mirror::Object>))
    REQUIRES_SHARED(Locks::mutator_lock_) {
  ScopedTrace trace(__FUNCTION__);
  return_type res = 0;  // On exception, the result will be ignored.
  ArtField* f =
      FindFieldFromCode<StaticPrimitiveRead, false>(field_idx,
                                                    referrer,
                                                    self,
                                                    primitive_type);
  if (LIKELY(f != nullptr)) {
    ObjPtr<mirror::Object> obj = f->GetDeclaringClass();
    res = (f->*func)(obj);
  }
  return res;
}

2.分析問(wèn)題

MterpGetStatic 就是去獲取一個(gè)類的靜態(tài)成員, 為什么會(huì)用掉 85 ms ?apk中dex文件會(huì)在art中轉(zhuǎn)化為一個(gè)DexFile對(duì)象谨履,而每一個(gè) DexFile 對(duì)象會(huì)對(duì)應(yīng)一個(gè) DexCache 對(duì)象欢摄。DexCache 的作用是用來(lái)緩存包含在一個(gè) dex 文件里面的類型 (Type), 方法 (Method), 域 (Field), 字符串 (String) 和靜態(tài)儲(chǔ)存區(qū) (Static Storage) 等信息。

art/runtime/mirror/dex_cache.cc

void DexCache::Init(const DexFile* dex_file,
         ObjPtr<String> 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<CallSite>* resolved_call_sites,
         uint32_t num_resolved_call_sites)
   REQUIRES_SHARED(Locks::mutator_lock_);

上面是 DexCache 的初始化函數(shù), num_resolved_fields 表示 DexCache 中緩存 Field 的個(gè)數(shù), 來(lái)打印一下這個(gè)參數(shù)

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 上, 一個(gè) DexCache 最多緩存 1024 個(gè) Field, 而實(shí)際上有上萬(wàn)個(gè) Filed, 導(dǎo)致 MterpGetStatic 的時(shí)候 cache 命中率極低, 最終導(dǎo)致 MterpGetStatic 耗時(shí)笋粟。

3.解決問(wèn)題

嘗試調(diào)整cache size與N一致怀挠,MterpGetStatic 明顯改善, 單一個(gè) inflate 就快了 80 ms, 優(yōu)化后高德地圖的啟動(dòng)時(shí)間可以減少 166 ms。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載害捕,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者绿淋。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市尝盼,隨后出現(xiàn)的幾起案子吞滞,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件裁赠,死亡現(xiàn)場(chǎng)離奇詭異殿漠,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)佩捞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門绞幌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人一忱,你說(shuō)我怎么就攤上這事莲蜘。” “怎么了帘营?”我有些...
    開(kāi)封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵菇夸,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我仪吧,道長(zhǎng)庄新,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任薯鼠,我火速辦了婚禮择诈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘出皇。我一直安慰自己羞芍,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布郊艘。 她就那樣靜靜地躺著荷科,像睡著了一般。 火紅的嫁衣襯著肌膚如雪纱注。 梳的紋絲不亂的頭發(fā)上畏浆,一...
    開(kāi)封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音狞贱,去河邊找鬼刻获。 笑死,一個(gè)胖子當(dāng)著我的面吹牛瞎嬉,可吹牛的內(nèi)容都是我干的蝎毡。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼氧枣,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼沐兵!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起便监,我...
    開(kāi)封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤扎谎,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體簿透,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡移袍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了老充。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片葡盗。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖啡浊,靈堂內(nèi)的尸體忽然破棺而出觅够,到底是詐尸還是另有隱情,我是刑警寧澤巷嚣,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布喘先,位于F島的核電站,受9級(jí)特大地震影響廷粒,放射性物質(zhì)發(fā)生泄漏窘拯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一坝茎、第九天 我趴在偏房一處隱蔽的房頂上張望涤姊。 院中可真熱鬧,春花似錦嗤放、人聲如沸思喊。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)恨课。三九已至,卻和暖如春岳服,著一層夾襖步出監(jiān)牢的瞬間剂公,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工派阱, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留诬留,地道東北人斜纪。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓贫母,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親盒刚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子腺劣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 前言 關(guān)于冷啟動(dòng)的優(yōu)化方法,網(wǎng)上已經(jīng)有很多的文章了因块,總結(jié)起來(lái)橘原,大概有以下幾種優(yōu)化方式: 優(yōu)化布局,這一步是最簡(jiǎn)單的...
    事多店閱讀 4,960評(píng)論 1 11
  • 請(qǐng)保持淡定,分析代碼趾断,記拙苊:性能很重要。 啟動(dòng)時(shí)間優(yōu)化 毫無(wú)疑問(wèn)芋酌,應(yīng)用的啟動(dòng)速度越快越好增显。 本文可以幫助你優(yōu)化應(yīng)用...
    Mupceet閱讀 11,373評(píng)論 5 19
  • 先講點(diǎn)題外話 簡(jiǎn)述Activity的幾種啟動(dòng)模式 standard標(biāo)準(zhǔn)啟動(dòng)模式同云,也是Activity的啟動(dòng)模式,以...
    大大大大大先生閱讀 6,676評(píng)論 0 3
  • 概要 應(yīng)用運(yùn)行時(shí)的卡頓問(wèn)題非常影響用戶體驗(yàn)堵腹,嚴(yán)重降低產(chǎn)品表現(xiàn)力炸站,本文將介紹應(yīng)用卡頓原因以及分析方法等等。 卡頓問(wèn)題...
    某昆閱讀 2,529評(píng)論 1 8
  • part2:重慶市區(qū) “在3D魔幻城市疚顷,我上演了自己的魔幻故事旱易。”--啊坨 Day6晚: 較場(chǎng)口夜市-八一好吃街-...
    啊坨閱讀 242評(píng)論 0 0