Android卡頓掉幀問題分析之實戰(zhàn)篇

努比亞技術團隊原創(chuàng)內(nèi)容茫藏,轉載請務必注明出處盗冷。

本文是Android卡頓問題分析三部曲的最后一篇虐译。在前面兩篇文章的理論和工具的分析基礎上鹤耍,本文將結合典型實戰(zhàn)案例夸溶,分析常見的造成卡頓等性能問題的原因夕晓。從系統(tǒng)工程師的總體角度來看 矾策,造成卡頓等性能問題的原因總體上大致分為三個大類:一類是流程執(zhí)行異常磷账;二是系統(tǒng)負載異常;三是編譯問題引起贾虽。

1 流程執(zhí)行異常

在前文Android卡頓掉幀問題分析之原理篇的內(nèi)容中逃糟,我們以滑動場景為例完整的分析了Android應用上幀顯示的全流程(默認開啟硬件繪制加速條件下)。下面我們用一張圖來看看這個流程上可能引起卡頓蓬豁、反應延遲等性能問題的點绰咽。

jank_reason.jpg

從上圖可以看出,在Android應用上幀顯示的各個流程上都可能出現(xiàn)問題導致出現(xiàn)卡頓地粪、反應延遲等性能問題取募。下面我們分類來分析一下各個流程上可能的引起卡頓等性能問題的點。

1.1 system_server引起應用卡頓

一蟆技、理論分析

框架system_server進程內(nèi)部的各個核心服務AMS玩敏、WMSPKMS等质礼,都有各自的對象鎖和工作線程聊品,各個服務運行時相互之間存在交互,可能出現(xiàn)某個線程持鎖執(zhí)行耗時操作而其它線程陷入長時間鎖等待的情況几苍。某些情況下當系統(tǒng)框架負載過重或流程出現(xiàn)異常情況下翻屈,其工作線程之間相互等鎖的現(xiàn)象會非常嚴重。如果此時應用AppUI線程中有Binder請求妻坝,而系統(tǒng)框架內(nèi)部處理請求的實現(xiàn)又恰好需要持鎖伸眶,就可能間接導致應用AppUI線程阻塞在Binder請求惊窖,而引起App應用的卡頓問題。
二厘贼、典型案例分析

問題描述:設備剛開機時界酒,桌面左右滑動出現(xiàn)卡頓。

問題分析:結合Systrace工具分析問題原因如下:

launcher_jank.jpg

從以上問題Systrace的分析可以看出嘴秸,出現(xiàn)桌面滑動卡頓的原因是:

  1. 桌面應用的UI線程(tid:4927)中通過Binder請求訪問框架PKMS服務的getActivityInfo接口查詢Activity的相關信息時出現(xiàn)長時間阻塞毁欣;
  2. 而框架PKMS服務中處理此Binder請求的線程(tid:5271)邏輯需要持鎖執(zhí)行,而當前此鎖對象的“鎖池”中至少已經(jīng)有7個線程都已經(jīng)在等待這把鎖岳掐,所以此線程進入鎖對象的“鎖池”中阻塞等待鎖釋凭疮;
  3. 而此時持有該鎖的線程為框架的android.bg工作線程(tid:2608),此線程中正持鎖執(zhí)行IO寫磁盤等耗時操作串述,且線程的優(yōu)先級較低执解,在剛開機階段系統(tǒng)整體負載比較重的場景下,搶不到CPU執(zhí)行時間片而長時間處于Runnable狀態(tài)纲酗,導致運行時間較長衰腌。

三、優(yōu)化思路

針對此類system_server內(nèi)部的各種鎖等待或流程異常導致的性能問題觅赊。Google在歷年的Android版本上也在不斷針對優(yōu)化右蕊,通過優(yōu)化流程盡量的減少流程中不必要的持鎖或減小持鎖范圍。例如在Android P上推出的LockFreeAnimation無鎖窗口動畫吮螺,就實現(xiàn)了無需持有WMS鎖播放窗口動畫尤泽,從而極大減少窗口動畫卡頓的概率;Android 12上在 PackageManager 中规脸,引入只讀快照減少了 92% 的鎖爭用坯约。另外各大手機廠商也會在自家的ROM系統(tǒng)上針對這種情況對框架作出持續(xù)的優(yōu)化,通過分析持鎖等待的關系莫鸭,找到問題源頭的當前持鎖線程闹丐,利用一些空間換時間的緩存方案或優(yōu)化CPUIO等資源的調(diào)度與分配被因,減少線程的持鎖的時長卿拴,從而改善其它線程鎖等待的時長

1.2 Input事件處理引起卡頓

一梨与、理論分析

從前面Android卡頓掉幀問題分析之原理篇文章中的分析我們知道堕花,屏幕驅(qū)動上報的Input事件要經(jīng)過框架的InputReaderInputDispatcher線程的讀取與分發(fā),然后通過Socket發(fā)送到應用進程中粥鞋,再經(jīng)過界面View控件樹的層層分發(fā)后消費處理缘挽。這個過程中任意一個流程出現(xiàn)阻塞就能造成用戶的觸控操作得不到及時的響應,出現(xiàn)用戶感知的系統(tǒng)反應延遲或卡頓現(xiàn)象。

二壕曼、典型案例分析

問題描述:微博應用界面手勢滑動退出時苏研,界面卡住幾秒鐘才有反應。

問題分析:結合Systrace工具分析問題原因如下:

input_jank.jpg

從以上問題Systrace的分析可以看出腮郊,退出應用界面時出現(xiàn)卡住幾秒的原因:

  1. 首先看Systracesystem_server進程中標識Input事件分發(fā)的"iq"“oq”隊列的信息摹蘑,可以看到框架InputDispatcher對事件的分發(fā)處理沒有出現(xiàn)阻塞或漏報等異常情況;
  2. 從微博應用對應的“wq”隊列可以看到轧飞,應用進程一直有沒有完成處理消費的Input事件衅鹿,說明問題在微博應用進程側;
  3. Systrace上微博應用側的信息过咬,可以看到應用UI線程很長一段的deliverInputEventtag(大概持續(xù)2.5秒左右)大渤,且“aq:ime”隊列中顯示有持續(xù)很長一段時間的事件處理邏輯。說明應該是在應用進程UI線程中判斷Input事件類型為返回鍵的Key事件援奢,所以先交給輸入法應用進行處理兼犯,但是輸入法進程一直沒有完成處理忍捡,導致應用UI線程一直處于等待狀態(tài)無法及時處理此Input事件集漾,說明問題出現(xiàn)在輸入法應用側;
  4. 然后我們用ps命令查看當前輸入法進程信息砸脊,發(fā)現(xiàn)輸入法進程(pid:5897)當前的狀態(tài)為“D”俊性,說明處于進程“凍結”的休眠狀態(tài)治筒,結合Systrace上顯示的輸入法進程的主線程信息顯示,發(fā)現(xiàn)其確實是一直處于Sleeping狀態(tài)。所以此問題出現(xiàn)的原因就是因為輸入法進程被異撤钢“凍結”導致的。和負責維護進程“凍結”功能的同事溝通并分析日志后發(fā)現(xiàn)倘感,問題的原因是用戶安裝并設置了新的輸入法應用留特,進程“凍結”模塊沒有成功識別,將其認定為普通后臺進程所以進行了“凍結”從而導致了異常孩哑。

到此還剩一個疑問就是為什么輸入法進程都被“凍結”而休眠了栓霜,應用UI線程在等待2.5秒后還是能繼續(xù)往下處理Input事件,并成功退出應用Activity界面呢横蜒?原因就是應用UI線程進入等待輸入法處理Input事件時會設置一個超時胳蛮,這個時長就是2.5秒,如果超時后輸入法進程還是沒有處理完丛晌,就會強行結束等待的邏輯仅炊,由應用UI線程繼續(xù)往下處理Input事件。相關源碼如下所示:

/*frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java*/
...
/**
* Timeout in milliseconds for delivering a key to an IME.
*/
static final long INPUT_METHOD_NOT_RESPONDING_TIMEOUT = 2500;
...
// Must be called on the main looper
int sendInputEventOnMainLooperLocked(PendingEvent p) {
        if (mCurChannel != null) {
           ...
            if (mCurSender.sendInputEvent(seq, event)) {
                ...
                Message msg = mH.obtainMessage(MSG_TIMEOUT_INPUT_EVENT, seq, 0, p);
                msg.setAsynchronous(true);
                mH.sendMessageDelayed(msg, INPUT_METHOD_NOT_RESPONDING_TIMEOUT);//1.處理Input事件時設置一個2500ms的超時檢查
                return DISPATCH_IN_PROGRESS;
            }
            ...
        }
      return DISPATCH_NOT_HANDLED;
}
...
case MSG_TIMEOUT_INPUT_EVENT: {
     finishedInputEvent(msg.arg1, false, true);// 2.時間到之后如果輸入法還是沒有完成Input事件處理澎蛛,則強行結束輸入法對事件的處理邏輯
     return;
}
...

三抚垄、優(yōu)化思路

對用戶觸控事件的響應速度直接關系到用戶對交互設備性能體驗的感知,所以一直以來都是系統(tǒng)性能優(yōu)化工作的重中之重。多年來谷歌督勺,包括SOC廠商都有針對系統(tǒng)Input事件的處理流程作出優(yōu)化渠羞,以提升觸控響應速度。例如高通基線上在2018年左右就有一筆提交智哀,優(yōu)化應用進程側的Input事件處理流程次询,大概思路就是識別應用UI線程中收到第一個ACTION_DOWNTouch事件后,調(diào)用sendMessageAtFrontOfQueue接口在應用UI線程的消息隊列的最前面插入一幀doFrame繪制任務瓷叫,這樣界面不用等待下一個Vsync的信號的到來就能直接上幀顯示屯吊,從而減少整個Input觸控事件的響應延遲。從Systrace上表現(xiàn)如下圖所示:

OPTS_INPUT.png

國內(nèi)各大手機廠商也有各自的優(yōu)化方案摹菠。比如硬件上采用觸控采樣率更高的屏幕盒卸,屏幕觸控采樣率達到240HZ甚至480HZ。再比如監(jiān)控到觸控事件后提升CPU主頻次氨,提升觸控事件處理相關線程和渲染線程的優(yōu)先級等方式蔽介,從而優(yōu)化事件觸控響應速度。而對于應用APP開發(fā)者來說煮寡,需要做的就是避免在Input觸控事件的分發(fā)處理流程中執(zhí)行耗時操作虹蓄,以免引起觸控延遲或卡頓等性能問題。

1.3 應用UI線程耗時引起卡頓

一幸撕、理論分析

這部分邏輯主要由應用APP開發(fā)者控制薇组。根據(jù)前文Android卡頓掉幀問題分析之原理篇中的分析可知,UI線程耗時過長必然會導致Vsync周期內(nèi)應用上幀出現(xiàn)超時坐儿。特別是屏幕高刷時代的到來律胀,留給應用UI線程處理上幀任務的時長越來越短。例如120HZ的高刷屏幕配上貌矿,一個Vsync信號周期已經(jīng)縮短到8ms左右炭菌。這要求應用APP開發(fā)者能寫出更加高性能質(zhì)量的代碼。應用UI線程耗時引起的卡頓往往涉及的因素比較多逛漫,下面列舉一些常見原因:

  1. UI線程消息隊列中存在除doFrame繪制任務外的其它耗時任務黑低,導致Vsync信號到來后,無法及時觸發(fā)UI線程執(zhí)行doFrame繪制上幀任務尽楔,而導致掉幀投储。例如界面布局XML文件的inflate解析,如果界面布局文件比較復雜阔馋,就到有大量的IO與反射等耗時操作玛荞。又或者UI線程中有decodeBitmap解析大圖片的耗時操作。
  2. UI幀的doFrame繪制任務處理耗時過長導致掉幀呕寝。最常見的問題就是應用界面布局存在過度繪制勋眯,導致measure/layout/draw任務的計算復雜度成倍上升。再比如應用界面布局中的部分View控件層面如果關閉了硬件繪制加速,就會觸發(fā)View#buildDrawingCache的耗時操作客蹋,從而導致整個draw動作耗時過長而引起掉幀塞蹭。
  3. UI線程存在大量的阻塞等待導致上幀超時UI線程陷入阻塞等待讶坯,常見的原因就是跨進程的Binder調(diào)用阻塞和進程內(nèi)的同步鎖競爭等待番电。還有一類情況就是頻繁的GC內(nèi)存回收引起(一般為進程內(nèi)存抖動或內(nèi)存泄露引起)。

二辆琅、典型案例分析

問題描述:三方應用書旗小說界面上下滑動嚴重掉幀卡頓漱办。

問題分析:結合Systrace工具分析問題原因如下:

buildDrawingCache_jank.png

Systrace上可以看到,應用UI線程上幀的doFrame流程中主要耗時點在buildDrawingCache的流程上婉烟。下面我們結合源碼來看看什么情況下draw流程中會出現(xiàn)buildDrawingCache的動作:

/*frameworks/base/core/java/android/view/View.java*/
...
public RenderNode updateDisplayListIfDirty() {
   ...
   if (layerType == LAYER_TYPE_SOFTWARE) { //判斷View的layerType為LAYER_TYPE_SOFTWARE娩井,也就是關閉了硬件繪制加速,執(zhí)行buildDrawingCache
       buildDrawingCache(true);
       ...
    }else{
       ...
       draw(canvas); //走硬件繪制加速的流程
       ...
    }
    ....
}
...

從代碼可以看出當判斷某個View空間的layerTypeLAYER_TYPE_SOFTWARE似袁,也就是關閉了硬件繪制加速的情況下洞辣,就會觸發(fā)走到buildDrawingCache的耗時邏輯中。所以此問題就是由于書旗小說應用的界面布局中一個名為OriWebViewExView控件關閉了硬件繪制加速導致昙衅。

問題描述Twitter應用界面上下滑動嚴重掉幀卡頓扬霜。

問題分析:結合Systrace工具分析問題原因如下:

app block.png

Systrace上可以看到,三方應用Twitter出現(xiàn)掉幀的主要原因是:其UI線程中存在大量耗時的inflate解析xml布局文件的操作绒尊,且出現(xiàn)問題時UI線程已經(jīng)持續(xù)RunningCPU 5大核心上但是還是出現(xiàn)嚴重的上幀超時畜挥。

三仔粥、優(yōu)化思路

針對應用UI線程耗時引起卡頓的問題婴谱,原則就是盡量減輕UI線程的負擔。針對不同的引起問題原因躯泰,常見的優(yōu)化思路大致如下:

  1. 異步處理:各種耗時操作盡量放到子線程異步處理谭羔。比如使用View的異步線程inflate方案;decodeBitmap加載圖片的耗時操作移到子線程統(tǒng)一處理等麦向。
  2. 邏輯優(yōu)化:必須要在主線程執(zhí)行的邏輯應盡量優(yōu)化瘟裸,減少計算頻次,避免重復計算诵竭。比如采用約束布局解決嵌套過多的問題话告,避免過度繪制;優(yōu)化應用的內(nèi)存占用卵慰,避免內(nèi)存泄露沙郭、內(nèi)存抖動等問題,從而減少GC觸發(fā)的次數(shù)裳朋;優(yōu)化內(nèi)部代碼邏輯病线,盡量減少主線程陷入同步鎖競爭等待的狀態(tài);原則上不要主動去關閉硬件繪制加速。
  3. 流程復用:能復用的邏輯盡量復用送挑,以避免多次調(diào)用產(chǎn)生的性能開銷绑莺。比如ListViewAdapter中實現(xiàn)View的復用,減少Viewinflate執(zhí)行次數(shù)惕耕。另外UI線程中Binder請求框架查詢一些系統(tǒng)信息纺裁,能夠一次查完就不要分多次執(zhí)行。且查詢結果應盡量緩存在內(nèi)存中實現(xiàn)復用司澎,避免多次反復的查詢造成主線程頻繁陷入Binder阻塞等待对扶。

1.4 SurfaceFlinger耗時引起卡頓

一、理論分析

從前文的分析可以知道惭缰,SurfaceFlingerAndroid系統(tǒng)的整個圖形顯示系統(tǒng)中是起到一個承上浪南、啟下的作用,負責收集不同應用進程Surface的繪制緩存數(shù)據(jù)進行的合成漱受,然后發(fā)送到顯示設備络凿。所以,如果SurfaceFlinger的處理流程上出現(xiàn)耗時或阻塞昂羡,必然會導致整個應用的上幀顯示的流程出現(xiàn)超時而掉幀絮记。特別是隨著屏幕高刷時代的到來,Vsync周期不斷縮短虐先,留給SurfaceFlinger處理的時間也越來越短怨愤,對SurfaceFlinger的性能提出了更高的要求。這也是谷歌蛹批、SOC廠商包括各大手機廠商做系統(tǒng)性能優(yōu)化的重點撰洗。

二、典型案例分析

問題描述:設置120HZ高刷模式下腐芍,手機錄屏差导、投屏場景下出現(xiàn)幀率下降。

問題分析:結合Systrace工具分析問題原因如下:

sf_block.png

Systrace上可以看到猪勇,開啟錄屏或投屏后设褐,部分Layer需要采用效率更低的GPU合成方式(主要是因為高通平臺HWC不支持回寫),SurfaceFlinger的主線程中需要實現(xiàn)drawLayers的動作泣刹,還需要將圖層數(shù)據(jù)寫入到MediaCodec緩存中助析,總體負載較高,導致在8ms120HZ高刷模式下)內(nèi)無法完成一幀數(shù)據(jù)的處理而出現(xiàn)幀率下降的問題椅您。

三外冀、優(yōu)化思路

針對SurfaceFlinger引起的性能卡頓問題,常見的優(yōu)化思路有:

  1. 谷歌通過cpuset的配置襟沮,默認場景下SurfaceFlinger只能使用4CPU小核心運行锥惋;部分廠商會在投屏或錄屏等負載比較重的場景下放開這個限制昌腰,讓SurfaceFlinger進程能跑到CPU大核心上。
  2. 部分廠商在開啟錄屏的情況下膀跌,會鎖定屏幕刷新率最高不超過60HZ遭商,以減輕SurfaceFlinger進程的壓力。
  3. Android 12上捅伤,谷歌就將各個應用對應的BufferQueue的管理工作移動到應用進程側處理劫流,以減輕SurfaceFlinger進程的負載。

2 系統(tǒng)負載異常

CPU丛忆、GPU祠汇、內(nèi)存、磁盤等都是系統(tǒng)內(nèi)各個進程任務的運行所必需的公共系統(tǒng)資源熄诡。這就涉及到一個復雜的資源調(diào)度與分配的問題可很,實現(xiàn)將系統(tǒng)資源與各個不同進程的需求進行合理的匹配,以保證進程任務能夠流暢的運行凰浮。這就涉及到兩個問題:

  1. 這些系統(tǒng)公共資源是否充足我抠。比如當進程需要時,當前CPU核心的運行主頻算力是否足夠高袜茧,系統(tǒng)剩余內(nèi)存是否足夠大菜拓,磁盤的讀寫速度是否足夠快。
  2. 這些系統(tǒng)資源的分配是否合理笛厦。比如當前優(yōu)先級更高的前臺應用進程相對于后臺進程是否能獲得更多的CPU調(diào)度運行時間片和I/O帶寬等資源纳鼎,以保證界面的流暢運行。

從軟件的角度出發(fā)裳凸,每個應用任務進程都希望自己獲得無限的CPU算力贱鄙、無限的內(nèi)存資源分配等能力,以保證其能夠流暢的運行登舞。但是從硬件的角度出發(fā)贰逾,各種系統(tǒng)的公共資源配置都是有限的悬荣,不可能無限的提供給每個任務進程菠秒,且在移動設備上還要考量功耗與發(fā)熱的問題,需要限制各種系統(tǒng)資源的供給氯迂。所以如何協(xié)調(diào)這種供需關系之間的矛盾践叠,將寶貴的系統(tǒng)資源合理的分配給當前最需要的任務進程,以最小的資源開銷保證系統(tǒng)的流暢運行嚼蚀,就是考驗操作系統(tǒng)的設計者和性能優(yōu)化工程師的功力的問題禁灼,弄不好就可能出現(xiàn)應用任務進程的運行受限于系統(tǒng)運行資源的分配而出現(xiàn)各種性能問題。

2.1 CPU調(diào)度引起卡頓

一轿曙、理論分析

CPU算力資源絕對是系統(tǒng)內(nèi)最重要的公共資源之一弄捕,每個應用程序的正常運行都需要CPU的調(diào)度執(zhí)行僻孝。目前的主流移動設備搭載的CPU基本上都是采用多核心架構設計,按照算力的大小設置有多顆CPU運算核心(以高通最新的驍龍8 gen 1處理器為例守谓,其擁有8CPU運算核心穿铆,包含四個小核、三個中核和一個大核斋荞,算力依次遞增荞雏,功耗也逐漸增大)。

8 gen 1.png

Linux操作系統(tǒng)內(nèi)核中會有相應的schedulergovernor調(diào)度器平酿,來管理每顆CPU核心上當前運行的任務的排程凤优,以及每顆CPU核心當前運行的主頻(每顆CPU核心都有一個運行主頻參數(shù)可以支持在一定的范圍內(nèi)動態(tài)調(diào)節(jié),主頻越高算力越強蜈彼,相應的功耗也就越大)筑辨、節(jié)能狀態(tài)等參數(shù),以達到最佳的性能與功耗的平衡幸逆。但是很多時候由于CPU運行核心上的任務排布不合理挖垛,或運行主頻過低導致的算力過低,無法滿足應用進程的需求秉颗,就可能導致應用出現(xiàn)卡頓掉幀等性能問題痢毒。常見的一些原因如下:

  1. 后臺應用進程任務持續(xù)運行搶占CPU運行資源,或者處于功耗管控直接關核蚕甥,導致前臺應用的UI線程任務無法及時的獲得CPU運行算力執(zhí)行哪替,長時間處于Runnable狀態(tài),而出現(xiàn)上幀超時掉幀問題菇怀;
  2. 某些時候出于功耗與發(fā)熱的考量凭舶,將CPU的運行主頻壓的過低,導致算力不足爱沟。應用的上幀線程上的任務需要Running很長一段時間帅霜,而出現(xiàn)上幀超時掉幀。

二呼伸、典型案例分析

問題描述:騰訊視頻應用中觀看視頻時身冀,播放彈幕卡頓。

問題分析:結合Systrace工具分析問題原因如下:

cpu block.jpg

Systrace上可以看到:騰訊視頻播放視頻時出現(xiàn)掉幀卡頓的原因括享,是因為其負責彈幕上幀的線程無法獲得CPU運行資源搂根,而長時間處于Runnable狀態(tài),導致上幀出現(xiàn)嚴重超時铃辖。
進一步分析原因發(fā)現(xiàn)存在兩個問題:一個就是看CPU Trace信息發(fā)現(xiàn)此時只有CPU 0-3四個小核心和一個CPU 4一個中核心在運行剩愧,而算力更強的CPU 5-7都處于關閉狀態(tài),導致系統(tǒng)總體算力受限娇斩;另一個問題就是前臺應用騰訊視頻進程內(nèi)的所有線程運行任務都只分配到CPU 4上運行仁卷,其它幾個小核心處于空閑狀態(tài)也沒有使用穴翩,導致騰訊視頻應用的運行嚴重受限。
最終經(jīng)過分析找到問題的根本原因就是:一锦积、關閉算力更強的CPU 5-7的原因藏否,是系統(tǒng)組為了降低用戶全屏看視頻場景下的功耗而故意為之;二充包、騰訊視頻只能運行在CPU 4上的原因副签,是因為該項目使用的是高通最新的SM8450平臺,該平臺采用ARM V9架構設計基矮,32位的應用只能使用CPU4-6三個中核運行淆储,而應用商店提供的騰訊視頻剛好就是32位的版本。
至此問題水落石出家浇,后面解決問題思路就是:一方面推動應用商店上架騰訊視頻64位版本本砰;二是系統(tǒng)組那邊需要針對32位的應用前臺全屏看視頻的場景下的CPU配置做出調(diào)整,不再全部關閉CPU 5-7三個算力較強的核心钢悲。

問題描述:桌面界面左右滑動出現(xiàn)卡頓掉幀点额。

問題分析:結合Systrace工具分析問題原因如下:

cpu block2.jpg

Systrace上可以看到:前臺應用桌面出現(xiàn)掉幀的原因是其UI線程搶不到CPU運算資源而長時間處于Runnable狀態(tài)。而此時莺琳,CPU上分布有大量后臺應用的任務持續(xù)運行还棱。出現(xiàn)這個問題的原因是國內(nèi)部分應用存在很多進程保活機制惭等,會在后臺持續(xù)運行珍手,并想方設法提升其進程的優(yōu)先級。所以在系統(tǒng)關閉了相關針對性的管控后就會出現(xiàn)問題辞做。

三琳要、優(yōu)化思路

針對CPU調(diào)度問題引起的性能卡頓問題,常見的優(yōu)化思路有:

  1. 基于cgroup的進程資源隔離秤茅。Android系統(tǒng)上繼承了linuxcgroup機制稚补,以控制進程的資源占用,讓重要的進程獲得更多的系統(tǒng)資源框喳。涉及的系統(tǒng)資源如下圖所示:

    cgroup.png

    其中cpuset(可以實現(xiàn)限制進程只能運行在指定的CPU核心上)课幕、cpuctl(可以實現(xiàn)控制前后臺進程的CPU用量)和freezer(可以實現(xiàn)控制進程處于凍結休眠狀態(tài),完全放棄CPU執(zhí)行權)就是對進程CPU資源的控制帖努。后面的top-app標識當前進程所屬的資源分組撰豺。cgroup機制中會將不同的運行進程分成不同的資源組,比如前臺應用屬于top-app組拼余,后臺應用屬于background組。不同的組可以支持配置不同的資源使用權限(比如通過cpuset的配置亩歹,background組中的進程被限制只能運行在cpu 0-3三個小核心上匙监,而處于top-app組中的進程可以使用所有的cpu核心資源)凡橱。以cpuset為例,如下圖所示:
    cgroup_2.png

    cgroup_3.png

    通過這種限制后臺不重要的進程的CPU資源占用亭姥,從而讓前臺重要的應用進程獲得更多的CPU資源(至于進程的前后臺優(yōu)先級定義稼钩,主要是由框架AMS服務來管理維護,通過寫進程的oom_score_adj值來標識)达罗。國內(nèi)各大手機廠商的系統(tǒng)性能優(yōu)化的策略中坝撑,有很大一部分工作都是依賴于cgroup機制來實現(xiàn)的,通過對系統(tǒng)資源精細的定義劃分粮揉,和應用進程優(yōu)先級與功能場景的精準識別巡李,來實現(xiàn)對資源的合理分配,達到性能與功耗的最佳平衡扶认。

  2. 基于場景識別的CPU調(diào)度策略配置侨拦。主要工作包括:

  • CPU基礎配置影響各核的性能輸出:包括CPU Frequency運行主頻調(diào)節(jié)、排程器大核小核工作分配策略upmigrate/downmigrate/sched_boost的調(diào)節(jié)(根據(jù)CPU各核心的任務負載辐宾,對線程任務進行大小核分布的遷移調(diào)整)狱从,以上都是通過Soc廠商提供的Perf服務接口設置參數(shù),去寫相關的設備節(jié)點實現(xiàn)叠纹,以高通平臺為例相關節(jié)點如下圖所示:

    cpu節(jié)點配置.png

    還有溫控策略的配置調(diào)整(出于功耗與發(fā)熱的考量楼眷,移動設備上溫控機制thermal-engine服務,會根據(jù)遍布設備內(nèi)部的各個溫度傳感器讀取的溫度值前鹅,按照一定的溫度閾值對CPU運行主頻進行限制灌危,以防止設備過熱);

    • 對場景的定義識別與策略配置:比如定義應用冷啟動場景冒窍,在框架進行識別递沪,并動態(tài)將CPU主頻進行拉升一段時間,以提升應用打開速度综液;定義視頻播放的場景款慨,在框架進行識別,然后對CPU主頻進行限頻谬莹,在保證播放流暢的前提下檩奠,盡量降低功耗和發(fā)熱;這塊也屬于國內(nèi)各大手機廠商的系統(tǒng)性能優(yōu)化的核心工作附帽。如何實現(xiàn)精確的場景定義與識別埠戳,精準控制CPU的性能輸出,用最小的功耗蕉扮,保證應用界面的流暢運行整胃,就是對廠商功力的考驗
  1. 對三方應用后臺相互喚醒和進程痹樱活機制的限制屁使,防止其在后臺持續(xù)運行搶占CPU算力資源在岂。這塊也是國內(nèi)各大手機廠商系統(tǒng)優(yōu)化的重點工作之一。

2.2 GPU調(diào)度引起卡頓

一蛮寂、理論分析

GPU算力資源是繼CPU算力外蔽午,系統(tǒng)內(nèi)另一個重要的公共資源,從前文Android應用上幀顯示的流程分析可知酬蹋,應用對每一幀畫面的渲染處理都離不開GPU算力的支撐及老。和CPU類似,設備的GPU也有自己的運行主頻(運行主頻越高范抓,算力越強骄恶,同時發(fā)熱也就越大),且支持在一定的范圍內(nèi)調(diào)節(jié)尉咕,以實現(xiàn)功耗和性能的最佳平衡叠蝇。以高通SM8450平臺所搭載的型號為Adreno 730GPU為例,其參數(shù)信息如下圖所示:

GPU.png

從上圖可以看到年缎,GPU支持在一定的范圍內(nèi)動態(tài)調(diào)節(jié)運行主頻悔捶,Soc廠商和國內(nèi)的手機廠商會根據(jù)任務負載和使用場景,動態(tài)調(diào)節(jié)GPU的工作頻率以實現(xiàn)功耗與性能的最佳平衡单芜。但是如果調(diào)節(jié)不合理蜕该,就可能引起性能問題,常見的一些原因如下:

  1. 部分大型游戲應用(如原神)需要大量的GPU算力進行游戲畫面的實時渲染處理洲鸠,如果系統(tǒng)的GPU運行主頻算力沒有及時的跟上來堂淡,無法滿足應用對GPU算力的需求就會導致游戲應用上幀時出現(xiàn)阻塞超時而掉幀;
  2. 部分應用在后臺利用GPU做一些圖像處理的并行計算扒腕,搶占了系統(tǒng)的GPU算力資源绢淀;就可能導致前臺應用繪制上幀時,由于GPU算力不足瘾腰,使其渲染線程陷入長時間阻塞等待GPU渲染完成的Fence信號的狀態(tài)皆的,最終出現(xiàn)上幀超時卡頓的問題。

二蹋盆、典型案例分析

問題描述:三方應用UC瀏覽器界面滑動卡頓费薄。

問題分析:結合Systrace工具分析問題原因如下:

GPU block.PNG

從上圖的Systrace可以看出,UC瀏覽器出現(xiàn)上幀超時的主要原因是栖雾,其RenderThread渲染線程長時間阻塞在queueBuffer上幀的流程上楞抡,而queueBuffer阻塞在SurfaceFlinger進程那邊的Binder響應,而SurfaceFlinger進程中又需要等待GPU那邊完成畫面渲染處理的fence信號的(從應用進程側的waiting for GPU completionsystrace tag也能看出)析藕。如下簡化代碼所示:

/*frameworks/native/libs/gui/BufferQueueProducer.cpp*/
status_t BufferQueueProducer::queueBuffer(int slot,
        const QueueBufferInput &input, QueueBufferOutput *output) {
        ATRACE_CALL();
        ...
         // Wait without lock held
    if (connectedApi == NATIVE_WINDOW_API_EGL) {
        // Waiting here allows for two full buffers to be queued but not a
        // third. In the event that frames take varying time, this makes a
        // small trade-off in favor of latency rather than throughput.
        lastQueuedFence->waitForever("Throttling EGL Production"); // 等待GPU fence信號
    }
    return NO_ERROR;
}  

也就是說召廷,問題出在系統(tǒng)的GPU算力資源比較緊張。后來分析應用掉幀的同一時間段系統(tǒng)的其它運行進程的systrace表現(xiàn),發(fā)現(xiàn)系統(tǒng)的圖庫應用正在執(zhí)行一些Bitmap圖像處理的動作柱恤。再結合日志分析后與負責圖庫的同事溝通数初,最終找到問題的根本原因所在:圖庫應用中有一個事物分類相冊的功能找爱,當時正在后臺使用GPU進行照片圖像的特征識別與處理的邏輯梗顺,搶占了系統(tǒng)GPU算力資源,從而導致前臺UC瀏覽器應用的渲染線程阻塞在等待GPUfence信號的流程而出現(xiàn)掉幀车摄。

三寺谤、優(yōu)化思路

針對GPU調(diào)度問題引起的性能卡頓問題,常見的優(yōu)化思路有:

  1. 基于場景的GPU調(diào)度策略配置吮播。這塊主要是結合前臺應用場景的需求变屁,動態(tài)調(diào)節(jié)GPU的運行主頻等參數(shù),以最小的功耗滿足應用的性能需求意狠。這塊考驗各大手機廠商的調(diào)教水平粟关,也是廠商進行游戲優(yōu)化調(diào)教的重點工作之一。
  2. 后臺應用進程搶占GPU算力資源的管控环戈。主要是限制后臺應用的運行闷板,防止其出現(xiàn)搶占GPU算力資源的行為,以保證前臺應用的流暢運行院塞。

2.3 低內(nèi)存引起卡頓

一遮晚、理論分析

內(nèi)存也絕對算的上是一個重要的系統(tǒng)公共資源,系統(tǒng)內(nèi)任何進程的正常運行都需要消耗一定的內(nèi)存資源拦止。一般來講設備的RAM硬件物理內(nèi)存的大小是固定且有限的(4G县遣、8G或12G等),而系統(tǒng)內(nèi)運行的所有進程都需要共享這些有限內(nèi)存資源汹族。如何用有限的內(nèi)存資源去滿足系統(tǒng)各個應用進程無限的需求萧求,這就涉及到一個系統(tǒng)內(nèi)存管理的邏輯。這是一個復雜的問題顶瞒,幾乎貫穿了整個Android系統(tǒng)的各個層級夸政。從Linux內(nèi)核到Art虛擬機,再到Framework框架搁拙,甚至到APP應用秒梳,都會有各自的內(nèi)存管理的策略。關于Android系統(tǒng)的內(nèi)存管理的詳細邏輯箕速,由于篇幅所限本文就不詳細展開酪碘。我們以systemui應用進程為例,來看看一個普通應用進程的內(nèi)存占用分布:

app memory.png

合理的內(nèi)存的分配與管理無論對系統(tǒng)和應用來講都至關重要盐茎。因為內(nèi)存問題既能引發(fā)穩(wěn)定性問題兴垦,也能引發(fā)卡頓性能問題。下面我們分別來分析一下:

  1. 內(nèi)存引起的穩(wěn)定性問題:

    • 因為系統(tǒng)總的內(nèi)存資源是有限的,為了防止單個應用進程無限制的占用內(nèi)存資源探越,Android系統(tǒng)設計了很多機制來遏制這種情況的產(chǎn)生狡赐。比如在虛擬機層面,谷歌設計對每個應用進程占用的Dalvik Heap的內(nèi)存大小做出了限制钦幔,超過一定的閾值(記錄在dalvik.vm.heapgrowthlimitproperty中枕屉,一般為256M),就會觸發(fā)OutOfMemeory的異常鲤氢,導致應用進程崩潰搀擂。此外Android系統(tǒng)還引入了lmkd低內(nèi)存守護機制,當系統(tǒng)總的可用內(nèi)存低于一定的閾值卷玉,就會按照進程優(yōu)先級哨颂,將占用內(nèi)存比較大的應用進程殺掉。
    • 32位的應用虛擬內(nèi)存的尋址空間最大為4G相种,當應用進程的虛擬內(nèi)存占用超過4G時威恼,就無法繼續(xù)為其分配內(nèi)存,產(chǎn)生OOM類型的報錯寝并,出現(xiàn)應用崩潰或黑屏等問題箫措。
    • 另外Android系統(tǒng)基于Linux內(nèi)核,也績承了Linux系統(tǒng)對應用進程的相關限制食茎。如果應用進程中開啟的線程過多(每創(chuàng)建一個空線程也會有一定的內(nèi)存開銷)蒂破,超過/proc/sys/kernel/threads-max中定義的上限,也會報出OOM異常導致應用進程崩潰别渔;再比如應用進程中打開的文件句柄過多附迷,超過/proc/pid/limits節(jié)點中的定義上限,也會報出OOM異常導致應用進程崩潰哎媚。如下圖所示:
      oom.png
  2. 內(nèi)存引起的性能性問題:

  • 當應用出現(xiàn)內(nèi)存抖動或內(nèi)存泄露的問題時喇伯,就會導致虛擬機的GC頻繁工作,甚至觸發(fā)stop-the-world事件拨与,造成應用的UI線程出現(xiàn)掛起阻塞稻据,最終導致上幀出現(xiàn)超時而掉幀。

  • 當后臺活躍運行的應用進程過多時买喧,會造成系統(tǒng)總體的剩余內(nèi)存偏低捻悯。此時主要有兩個問題可能會造成應用的卡頓:

    a、當內(nèi)存zone處于低水位時淤毛,內(nèi)核會頻繁喚醒kswapd的線程起來執(zhí)行內(nèi)存回收的動作今缚;另外內(nèi)核PSI內(nèi)存壓力信號也會頻繁喚醒lmkd進程,進行掃描和殺進程低淡。這些動作都會占用CPU運行資源姓言,導致前臺應用UI線程由于搶不到運行CPU資源而出現(xiàn)長時間處于Runnable的問題瞬项;

    b、當系統(tǒng)內(nèi)存不足時何荚,kswapd的線程被喚醒進行內(nèi)存回收囱淋,會將page cache內(nèi)存頁中已經(jīng)緩存的磁盤文件信息進行大量釋放,導致應用進程中的一些IO讀文件操作(比如 view 讀文件餐塘,讀配置文件妥衣、讀 odex 文件等),由于page cache無法命中唠倦,觸發(fā)缺頁中斷称鳞,陷入內(nèi)核態(tài)阻塞等待涮较,一直等到readahead機制從磁盤中將文件內(nèi)容全部讀取出來才能釋放返回稠鼻。當大量進程在進行IO操作時,又進一步加重底層IO的阻塞時長狂票。最終導致前臺應用的UI線程長時間處于Uninterruptible Sleep - Block I/O的狀態(tài)而出現(xiàn)上幀超時卡頓問題候齿。

二、典型案例分析

問題描述:在三方應用唯品會的首頁界面上下滑動嚴重掉幀卡頓闺属。

問題分析:結合Systrace工具分析問題原因如下:

lowmemory block.jpg

Systrace上可以看到慌盯,唯品會應用出現(xiàn)掉幀卡頓的主要原因如下:

  1. 系統(tǒng)內(nèi)存緊張,頻繁喚醒kswapd0線程進行內(nèi)存回收操作掂器,其持續(xù)搶占CPU 5大核心運行亚皂,導致應用的UI線程和RenderThread渲染線程頻繁出現(xiàn)搶不到CPU資源得不到執(zhí)行,而處于Runnable的狀態(tài)国瓮,從而導致應用上幀超時而掉幀灭必;

  2. 系統(tǒng)內(nèi)存緊張時,page cache磁盤文件緩存被內(nèi)存回收乃摹,導致應用的UI線程和RenderThread渲染線程中的一些IO讀文件操作無法命中page cache緩存而頻繁陷入內(nèi)核Uninterruptible Sleep - Block I/O的狀態(tài)禁漓,導致應用上幀超時而掉幀。

三孵睬、優(yōu)化思路

針對內(nèi)存問題引起的性能卡頓問題播歼,常見的優(yōu)化思路有:

  1. 在應用進程側:作為應用開發(fā)者:

    • 需要解決應用自身的內(nèi)存泄漏問題,避免頻繁的內(nèi)存分配回收出現(xiàn)的內(nèi)存抖動現(xiàn)象掰读;

    • 并盡量優(yōu)化應用的內(nèi)存占用秘狞,包括優(yōu)化圖片加載占用、采用內(nèi)存更高效的數(shù)據(jù)結構(如ArrayMap)蹈集、采用對象池和圖片復用等內(nèi)存復用機制烁试、功能比較復雜的應用采用多進程方案、盡量優(yōu)化應用APK包體積大小雾狈、監(jiān)聽onTrimMemory事件并及時釋放內(nèi)存中的不必要緩存等廓潜;

    • 盡量不要做一些應用進程保活的機制,以免增加系統(tǒng)總體的內(nèi)存負擔辩蛋;

    • 將應用升級適配到64位版本呻畸,以免陷入虛擬內(nèi)存耗盡而進程崩潰的局面。

    使用Android Studio自帶的Profile分析工具進行內(nèi)存分析與優(yōu)化悼院,具體用法可以參考這篇文章:http://www.reibang.com/p/72b213fa6a26伤为。

  2. 在系統(tǒng)總體層面:這部分內(nèi)容主要屬于谷歌和手機廠商的工作,常見的一些針對性優(yōu)化手段有:

    • 調(diào)整lowmemorykiller的殺進程的門限值据途,讓其提前介入清理掉一些低優(yōu)先級的進程而釋放內(nèi)存绞愚,以免系統(tǒng)陷入低內(nèi)存狀態(tài);

    • 適當提高 extra_free_kbytes 值颖医,讓kswapd線程能提前介入進行內(nèi)存回收位衩,以免系統(tǒng)陷入低內(nèi)存狀態(tài);

    • 框架中設計后臺應用查殺功能熔萧,定期清理后臺低優(yōu)先級應用進程糖驴,并限制三方應用的后臺保活機制佛致,從而釋放內(nèi)存贮缕,以免系統(tǒng)陷入低內(nèi)存狀態(tài);

    • 通過cpuset配置限制kswapd不讓其使用部分CPU大核俺榆,從而讓出CPU資源給前臺應用的UI和渲染線程使用感昼;

    • 配置和啟動ZRAMapp compaction后臺內(nèi)存壓縮機制,從而減少應用進程后臺的內(nèi)存占用罐脊,騰出更多的系統(tǒng)可用內(nèi)存定嗓;

    • 優(yōu)化系統(tǒng)的磁盤IO性能,采用UFS3.1等性能更強的存儲器爹殊,以減輕系統(tǒng)出現(xiàn)內(nèi)存低的情況時蜕乡,出現(xiàn)IO阻塞的時長。

2.4 磁盤I/O引起卡頓

一梗夸、理論分析

磁盤是現(xiàn)代計算機系統(tǒng)實現(xiàn)數(shù)據(jù)持久化存儲的核心硬件模塊层玲。也是繼CPU和內(nèi)存外系統(tǒng)內(nèi)另一個重要的公共資源。Android系統(tǒng)內(nèi)應用程序的運行一般都會伴隨有大量的磁盤I/O讀寫的操作反症,比如讀寫數(shù)據(jù)庫辛块、加載XML布局文件、讀取配置文件等铅碍。但是相對于CPU和內(nèi)存相比润绵,磁盤的讀寫訪問速度是比較慢的,很容易成為CPU運行的瓶頸胞谈,導致系統(tǒng)被拖累尘盼。當應用進程的線程中發(fā)生直接I/O磁盤讀寫時憨愉,就會導致線程進入Uninterrupt Sleep-Block I/O阻塞狀態(tài),如下圖所示:

IO block.png

在開始分析磁盤I/O讀寫的瓶頸引起性能問題原因之前卿捎,我們先借用一張簡化圖來總體看看Linux系統(tǒng)的I/O棧:
linux IO.jpg

從上圖可以看出配紫,整個Linux 下的 I/O棧大致有三個層次:

  1. 文件系統(tǒng)層,以 write 為例午阵,內(nèi)核拷貝了 write 參數(shù)指定的用戶態(tài)數(shù)據(jù)到文件系統(tǒng) Cache 中躺孝,并適時向下層同步。
  2. 塊層底桂,管理塊設備的 I/O 隊列植袍,對 I/O 請求進行合并、排序籽懦。
  3. 設備層于个,通過 DMA 與內(nèi)存直接交互,完成數(shù)據(jù)和具體存儲設備之間的交互猫十。

可能存在的性能瓶頸問題如下:

  1. 為了優(yōu)化文件讀寫的性能览濒,減少磁盤的I/O 操作,Linux內(nèi)核系統(tǒng)提供了頁緩存(Page Cache)技術拖云,以頁為單位將磁盤上的文件內(nèi)容緩存在內(nèi)存中,從而提供了接近于內(nèi)存讀寫速度的文件順序讀寫速度应又,也就是Buffered I/O宙项。但是當系統(tǒng)剩余內(nèi)存不足時,系統(tǒng)會回收Page Cache頁緩存內(nèi)存株扛,導致文件讀寫操作直接落到磁盤上尤筐,從而極大降低I/O性能。這也就前一小節(jié)中分析的低內(nèi)存導致的性能卡頓問題洞就。
  2. 上層所有的I/O 請求都會放入隊列中進行合并排隊處理盆繁,這樣就可能引發(fā)不同應用進程之間的資源搶占問題。比如后臺進程存在大量讀寫磁盤的操作請求旬蟋,占用系統(tǒng)的I/O 資源油昂,這就會導致前臺應用的I/O 請求出現(xiàn)在隊列中阻塞排隊等待的現(xiàn)象,最終導致掉幀卡頓問題倾贰。
  3. 文件系統(tǒng)的性能瓶頸冕碟。比如針對NAND閃存特性設計的f2fs文件系統(tǒng),相比于ext4文件系統(tǒng)匆浙,能夠提升小文件的隨機讀寫性能安寺,并能減少碎片化的問題。再比如Android 11上引入的Fuse文件系統(tǒng)首尼,增加I/O讀寫的開銷挑庶,導致sdcard路徑下的部分路徑文件的I/O讀寫性能下降言秸。
  4. 閃存的讀寫性能瓶頸一個就是閃存硬件的讀寫性能差異迎捺,比如UFS 3.0相對于UFS 2.1來講順序讀取速度快90%井仰,順序?qū)懭胨俣雀翘嵘?code>160%;另外一個問題就是閃存重復寫入破加,需要先進行擦除操作俱恶,這樣隨著時間的推移,磁盤碎片變多范舀,剩余空間少合是,一個內(nèi)存頁的寫入會引起整個磁盤塊的遷移,造成耗時操作锭环,也就是早期Android手機給人形成“越用越卡”的印象的原因之一聪全。

二、優(yōu)化思路

針對I/O磁盤性能的瓶頸辅辩,總體優(yōu)化思路從兩個角度來看:

  1. 從手機廠商角度:
    • 采用更大的內(nèi)存硬件配置难礼,并從總體上優(yōu)化系統(tǒng)的內(nèi)存占用。以防止出現(xiàn)系統(tǒng)低內(nèi)存狀態(tài)下玫锋,Page Cache頁緩存被回收后蛾茉,應用直接寫磁盤而導致的I/O阻塞卡頓問題;
    • 啟用Linuxcgroup機制對I/O資源的進程隔離機制blkio撩鹿,將進程按照優(yōu)先級進行分組(分為前臺進程谦炬、后臺進程、甚至可以根據(jù)需求進行更詳細的自定義劃分)节沦,以分配不同的I/O帶寬資源键思,從而解決后臺應用進程搶占前臺應用I/O資源造成的卡頓的問題;
    • 采用f2fs文件系統(tǒng)甫贯,替換ext4文件系統(tǒng)吼鳞,以提升文件系統(tǒng)的性能;可以針對部分系統(tǒng)文件管理類的應用不走默認的fuse路徑叫搁,而是走f2fs赔桌,以提升其讀寫性能表現(xiàn);
    • 采用讀寫性能更強的閃存器件常熙,比如最新的UFS 3.1纬乍;
    • 啟用fstrim磁盤碎片整理功能,在息屏裸卫、充電等條件下觸發(fā)磁盤碎片整理的動作仿贬,從而減少磁盤碎片,提升磁盤讀寫性能墓贿;系統(tǒng)手機管家模塊可以引導用戶定期進行垃圾文件的清理茧泪,以保證磁盤有充足的剩余空間蜓氨;
    • 基于mmap內(nèi)存映射機制,將系統(tǒng)運行常訪問的文件Lock鎖定到內(nèi)存緩存中队伟,以提升文件讀寫的性能穴吹,減少真正發(fā)生I/O磁盤讀寫的概率,減輕系統(tǒng)的I/O負載嗜侮。Android系統(tǒng)框架中的PinnerService服務主要職責就是這個港令,如下圖所示:
      pinner.png

2.從應用開發(fā)者角度:

  • 選擇合適的文件讀寫操作。對讀寫速度有較高的要求锈颗,并允許低概率的數(shù)據(jù)丟失問題顷霹,就采用系統(tǒng)默認的基于Page Cache的緩存I/O;對文件讀寫的速度要求不高击吱,但是需要嚴格的保證數(shù)據(jù)不會丟失就使用Direct I/O淋淀;如果需要對同一塊區(qū)域進行頻繁讀寫的情況,對讀寫性能要求極高覆醇,可以采用mmap 內(nèi)存映射機制實現(xiàn)讀寫文件朵纷,比如騰訊開源的用于改善原生SharePreferences機制性能的存儲工具MMKV就是這個原理實現(xiàn)的。
  • I/O 方式的選擇永脓。一個是對于阻塞I/O操作盡量放入子線程執(zhí)行袍辞,以免阻塞UI線程;二是適當采用異步I/O減少讀取文件的耗時憨奸,提升CPU整體利用率革屠,比如Okio就支持異步I/O操作。
  • 優(yōu)化數(shù)據(jù)結構排宰,建立內(nèi)存緩存海渊,盡量減少不必要的I/O挎袜;

3 編譯引起卡頓性能問題

編譯引起的性能問題主要分為兩類:一類是代碼的解釋執(zhí)行耗時蔼水,運行時VerifyClass執(zhí)行耗時等引發(fā)卡頓氨距;二類是編譯本身引起的卡頓問題斋否,比如dex2oat編譯搶占CPU算力資源裙品,進程中的Jit編譯線程搶占CPU算力資源段直。

3.1 代碼解釋執(zhí)行耗時

一寓落、理論分析

Art虛擬機有兩種代碼執(zhí)行模式:quick code 模式和 Interpreter 模式呛谜。對于dex字節(jié)碼文件采用的是Interpreter解釋執(zhí)行的模式在跳,每次執(zhí)行代碼,虛擬機需要將代碼轉換成機器指令集隐岛,然后交給CPU去執(zhí)行猫妙,所以執(zhí)行效率比較低(早期Android系統(tǒng)被詬病性能差的原因之一)。而對于經(jīng)過編譯(AOTdex2oat編譯或JIT運行時及時編譯聚凹,都需要消耗一定的磁盤空間存儲編譯出來的機器碼文件)后生成的ELF格式機器碼文件(如.oat格式文件割坠、.art格式文件)齐帚,采用quick code模式,直接執(zhí)行arm 匯編指令彼哼,執(zhí)行效率較高对妄。為了做到性能、磁盤空間占用以及應用安裝速度之間的最佳平衡敢朱,從Android 7.0 開始剪菱,采用AOT+JIT+解釋執(zhí)行的混合模式,其特點如下:

  1. 應用在安裝的時候dex中大部分函數(shù)代碼不會被編譯拴签。
  2. App運行時孝常,dex文件先通過解析器被直接執(zhí)行,熱點函數(shù)會被識別并被JIT編譯后存儲在 jit code cache 中并生成profile文件以記錄熱點函數(shù)的信息篓吁。
  3. 手機進入 IDLE(空閑) 或者 Charging(充電) 狀態(tài)的時候茫因,系統(tǒng)會掃描 App 目錄下的 profile 文件并執(zhí)行 AOT 過程進行編譯。

所以ART虛擬機在執(zhí)行一個函數(shù)過程中會在 Interpreter 解釋器模式和quick code模式切換杖剪,具體切換規(guī)則如下圖所示:

JIT工作流程.png

從上圖可以看出冻押,有些時候應用部分代碼還是按照Interpreter 模式執(zhí)行,運行效率較低盛嘿,從而產(chǎn)生一些性能問題洛巢。

二、典型案例分析

問題描述:淘寶等三方應用冷啟動速度慢于同平臺競品機型次兆。

問題分析:結合Systrace工具分析問題原因如下:

with_jit.jpg

without_jit.jpg

從與競品機器的對比Systrace分析可以看到:同一個應用稿茉,且在兩臺手機CPU型號和運行主頻一致的情況下,競品機器上應用冷啟動期間芥炭,UI線程的Running時長更短漓库,且Jit工作線程上的熱點代碼編譯任務明顯少很多。從而可以推斷出园蝠,問題原因在于競品機器上的應用代碼大部分是以機器碼直接執(zhí)行渺蒿,而我們是以字節(jié)碼解釋執(zhí)行,所以效率更低彪薛,運行時間更長茂装。為什么會出現(xiàn)這個差異呢?后來對比日志分析發(fā)現(xiàn)善延,原因是競品機上應用第一次安裝打開后一段時間后少态,系統(tǒng)會觸發(fā)一次speed-profile模式的dex2oat編譯,會根據(jù)應用運行期間JIT搜集到的熱點函數(shù)信息保存后生成的Profile描述文件進行編譯易遣,從而將應用啟動期間的大部分代碼函數(shù)編譯成了機器碼.oat文件彼妻,后續(xù)再啟動應用時即可直接以quick code 模式執(zhí)行機器碼,所以執(zhí)行效率更高训挡。

三澳骤、優(yōu)化思路

針對代碼解釋執(zhí)行造成的性能問題歧强,大致優(yōu)化思路是盡量讓應用代碼以機器碼形式運行,部分有效的手段有:

  1. 部分手機廠商为肮,在手機預置的應用商店中下載的三方應用摊册,除了APK文件外,還會包含保存有應用熱點函數(shù)信息的Profile描述文件颊艳,這樣在應用dex2oat安裝時茅特,選擇speed-profile模式,就會根據(jù)Profile描述文件棋枕,將應用的大部分熱點函數(shù)直接編譯成機器碼白修,從而極大的提升應用的性能表現(xiàn)。
  2. 在手機息屏充電重斑,進入Idle狀態(tài)后兵睛,通過JobScheduler機制,啟動后臺服務窥浪,掃描 App 目錄下的 profile 文件并執(zhí)行 AOT 過程進行編譯祖很,以達到手機越用越快的效果。

3.2 編譯本身耗時

一漾脂、理論分析

dex2oat進程編譯應用的過程中假颇,系統(tǒng)會啟動很多線程,消耗大量的CPU算力資源骨稿”考Γ可能會導致前臺應用由于搶不到CPU算力資源,其UI線程長時間處于Runnable狀態(tài)而卡頓坦冠。另外應用進程內(nèi)部形耗,Jit工作線程動態(tài)編譯時持續(xù)運行,如果負載較重辙浑,持續(xù)跑到CPU大核上趟脂,也會造成CPU算力資源搶占。

二例衍、典型案例分析

問題描述:抖音應用界面嚴重卡頓。

問題分析:結合Systrace工具分析問題原因如下:

dex2oat block.jpg

從上圖可以看出:抖音應用界面卡頓的原因是因為其UI線程搶不到CPU算力資源而長時間處于Runnbale狀態(tài)已卸,而導致這個問題的很大一部分原因就是此時后臺dex2oat進程創(chuàng)建多個線程持續(xù)執(zhí)行應用編譯動作搶占CPU算力資源佛玄。

三、優(yōu)化思路

針對編譯本身耗時引起的性能問題累澡,部分有效的手段有:

  1. 通過cpuset配置梦抢,限制dex2oat進程和Jit工作線程對CPU的使用,使其不搶占CPU大核心算力資源愧哟,并設置參數(shù)限制其創(chuàng)建的線程數(shù)奥吩。
  2. dex2oat編譯應用的動作盡量放在設備息屏哼蛆,設備處于Idle狀態(tài)下進行,以免影響前臺用戶的操作霞赫;
  3. 限制三方應用在后臺觸發(fā)執(zhí)行dex2oat編譯動作(從Android 10 開始腮介,谷歌官方通過Selinux權限的管控,禁止了三方應用觸發(fā)dex2oat的權限)端衰;

參考

Linux系統(tǒng)中I/O操作的數(shù)據(jù)讀寫流程介紹 https://zhuanlan.zhihu.com/p/435406445
一文讀懂直播卡頓優(yōu)化那些事兒 https://mp.weixin.qq.com/s?__biz=MzI1MzYzMjE0MQ==&mid=2247491520&idx=1&sn=dbd14f1d0d6107c87c137433ef435b5b&chksm=e9d0d422dea75d34e9788b0355a08132f84e558a6a7fc46bfb8a730f8be80ea77abee1c66e63&mpshare=1&scene=1&srcid=0328FFMqSCGN58JthxnZbEVw&sharer_sharetime=1648435053999&sharer_shareid=3accc00cd7dff7038611325ccc9f75bd&version=4.0.2.6061&platform=win#rd

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末叠洗,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子旅东,更是在濱河造成了極大的恐慌灭抑,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抵代,死亡現(xiàn)場離奇詭異腾节,居然都是意外死亡,警方通過查閱死者的電腦和手機荤牍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門案腺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人参淫,你說我怎么就攤上這事救湖。” “怎么了涎才?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵鞋既,是天一觀的道長。 經(jīng)常有香客問我耍铜,道長邑闺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任棕兼,我火速辦了婚禮陡舅,結果婚禮上,老公的妹妹穿的比我還像新娘伴挚。我一直安慰自己靶衍,他們只是感情好,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布茎芋。 她就那樣靜靜地躺著颅眶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪田弥。 梳的紋絲不亂的頭發(fā)上涛酗,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機與錄音,去河邊找鬼商叹。 笑死燕刻,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的剖笙。 我是一名探鬼主播卵洗,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼枯途!你這毒婦竟也來了忌怎?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤酪夷,失蹤者是張志新(化名)和其女友劉穎榴啸,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體晚岭,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡鸥印,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了坦报。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片库说。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖片择,靈堂內(nèi)的尸體忽然破棺而出潜的,到底是詐尸還是另有隱情,我是刑警寧澤字管,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布啰挪,位于F島的核電站,受9級特大地震影響嘲叔,放射性物質(zhì)發(fā)生泄漏亡呵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一硫戈、第九天 我趴在偏房一處隱蔽的房頂上張望锰什。 院中可真熱鬧,春花似錦丁逝、人聲如沸汁胆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽沦泌。三九已至,卻和暖如春辛掠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工萝衩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留回挽,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓猩谊,卻偏偏與公主長得像千劈,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子牌捷,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

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