偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

抖音 Android 性能優(yōu)化系列:Java 鎖優(yōu)化

原創(chuàng) 精選
移動開發(fā) 移動應用
本文將著重向大家介紹 Slardar 線上鎖監(jiān)控方案的原理與使用方法,以及我們在抖音上發(fā)現(xiàn)的鎖的經典案例與優(yōu)化實踐。

背景

Java 多線程開發(fā)中為了保證數(shù)據的一致性,引入了同步鎖(synchronized)。但是,對鎖的過度使用,可能導致卡頓問題,甚至 ANR:

  • Systrace 中的主線程因為等鎖阻塞了繪制,導致卡頓

圖片

圖片

  • Slardar 平臺(字節(jié)跳動內部 APM 平臺,以下簡稱 Slardar)中搜索 waiting to lock 關鍵字發(fā)現(xiàn)很多鎖導致的 ANR,僅 Java 鎖異常占到總 ANR 的 3.9%

圖片

本文將著重向大家介紹 Slardar 線上鎖監(jiān)控方案的原理與使用方法,以及我們在抖音上發(fā)現(xiàn)的鎖的經典案例與優(yōu)化實踐。

監(jiān)控方案

獲取運行時鎖信息的方法有以下幾種

方案

應用范圍

特點

systrace

線下

  1. 可以發(fā)現(xiàn)鎖導致的耗時
  2. 沒有調用棧

定制 ROM

線下

  1. 可以支持調用棧
  2. 修改 ROM 門檻較高,僅支持特定機型

JVMTI

線下

  1. 只支持 Android8+ 設備
  2. 不支持 release 包,且性能開銷較大

考慮到,很多鎖問題需要一定規(guī)模的線上用戶才能暴露出來,另外沒有調用棧難以從根本上定位和解決線上用戶的鎖問題。最終我們自研了一套線上鎖監(jiān)控系統(tǒng),它需要滿足以下要求:

  • 線上監(jiān)控方案
  • 豐富的鎖信息,包括 Java 調用棧
  • 數(shù)據分析平臺,包括聚合能力,設備和版本信息等
  • 可納入開發(fā)和合碼流程,防止不良代碼上線

這樣的鎖監(jiān)控系統(tǒng),能夠幫助我們高效定位和解決線上問題,并實現(xiàn)防劣化。

鎖監(jiān)控原理

我們先從 Systrace 入手,有一類常見的耗時叫做 monitor contention,其實是 Android ART 虛擬機輸出的鎖信息。

圖片

簡單介紹一下里面的信息

monitor contention with owner work_thread (27176) at android.content.res.Resources android.app.ResourcesManager.getOrCreateResources(android.os.IBinder, android.content.res.ResourcesKey, java.lang.ClassLoader)(ResourcesManager.java:901) waiters=1 blocking from java.util.ArrayList android.app.ActivityThread.collectComponentCallbacks(boolean, android.content.res.Configuration)(ActivityThread.java:5836)
  • 持鎖線程:work_thread
  • 持鎖線程方法:android.app.ResourcesManager.getOrCreateResources(...)
  • 等待線程 1 個
  • 等鎖方法:android.app.ActivityThread.collectComponentCallbacks(...)

Java 鎖,無論是同步方法還是同步塊,虛擬機最終都會到 MonitorEnter。我們關注的 trace 是 Android 6 引入的, 在鎖的開始和結束時分別調用ATRACE_BEGIN(...)? 和 ATRACE_END()

圖片

圖片

線上方案

默認情況下 atrace 是關閉的,開關在 ATRACE_ENABLED() 中。我們通過設置 atrace_enabled_tags 為 ATRACE_TAG_DALVIK 可以開啟當前進程的 ART 虛擬機的 atrace。

再看 ATRACE_BEGIN(...)? 和 ATRACE_END() 的實現(xiàn),其實是用 write 將字符串寫入一個特殊的 atrace_marker_fd (/sys/kernel/debug/tracing/trace_marker)。

圖片

圖片

因此通過 hook libcutils.so 的 write 方法,并按 atrace_marker_fd 過濾,就實現(xiàn)了對 ATRACE_BEGIN(...)? 和 ATRACE_END()? 的攔截。有了 BEGIN 和 END 后可以計算出阻塞時長,解析 monitor contention with owner... 日志可以得到我們關注的 Java 鎖信息。

獲取堆棧

到目前為止,我們已經可以監(jiān)控到線上用戶的鎖問題。但是還不夠,為了能夠優(yōu)化鎖的性能,我們想需要知道等鎖的具體原因,也就是 Java 調用棧。

獲取 Java 調用棧,可以使用Thread.getStackTrace()方法。由于我們 hook 住了虛擬機的等鎖線程,此時線程處于一種特殊狀態(tài),不可以直接通過 JNI 調用 Java 方法,否則導致線上 crash 問題。

圖片

解決方案是異步獲取堆棧,在 MonitorBegin 的時候通知子線程 5ms 之后抓取堆棧,MonitorEnd 計算阻塞時長,并結合堆棧數(shù)據一起放入隊列,等待上報 Slardar。如果 MonitorEnd 時不滿足 5ms 則取消抓棧和上報

圖片

數(shù)據平臺

由于方案本身有一定性能開銷,我們僅對灰度測試中的部分用戶開啟了鎖監(jiān)控。配置線上采樣后,命中的用戶將自動開啟鎖監(jiān)控,數(shù)據上報 Slardar 平臺后就可以消費了。

圖片

具體 case 可以看到設備信息、阻塞時長、調用堆棧

圖片

根據調用棧查找源碼,可以定位到是哪一個鎖,說明上報數(shù)據是準確的。

圖片

穩(wěn)定性方面,10 萬灰度用戶開啟鎖監(jiān)控后,無新增穩(wěn)定性問題。

圖片

圖片

優(yōu)化實踐

經過多輪鎖收集和治理,我們取得了一些不錯的收益,這里簡單介紹下鎖治理的幾個典型案例。

典型案例

inflate 鎖:?

先解析一下什么是 inflate:Android 中解析 xml 生成 View 樹的過程就叫做 inflate 過程。inflate 是一個耗時過程,常規(guī)的手段就是通過異步來減少其在主線程的耗時,這樣大大的減少了卡頓、頁面打開和啟動時長;但此方式也會帶來新的問題,比如 LayoutInflater 的 inflate 方法中有加鎖保護的代碼塊,并行構建會造成鎖等待,可能反而增加主線程耗時,針對這個問題有三種解決方案:

克隆 LayoutInflater

  • 把線程分為三類別:Main、工作線程和其它線程(野線程),Context(Activity 和 App)為每個類別提供專有 LayoutInflater,這樣能有效的規(guī)避 inflate 鎖。
  • 優(yōu)點:實現(xiàn)簡單、兼容性好
  • 缺點:LayoutInflater 中非安全的靜態(tài)屬性在并發(fā)情況下有概率產生穩(wěn)定性問題

code 構造替代 xml 構造

  • 這種方式完美的繞開了 inflate 操作,極大提高了 View 構造速度。
  • 優(yōu)點:復雜度高、性能好
  • 缺點:影響編譯速度、View 自定義屬性需要做轉換、存在兼容性問題(比如廠商改屬性)

定制 LayoutInflater

  • 自定義 FastInflater(繼承自 LayoutInflater)替換系統(tǒng)的 PhoneLayoutInflater,重寫 inflate 操作,去掉鎖保護;從統(tǒng)計數(shù)據看,在并發(fā)時快了約 4%。
  • 優(yōu)點:復雜度高、性能好
  • 缺點:存在兼容性,比如華為的 Inflater 為 HwPhoneLayoutInflater,無法直接替換。

圖片文件目錄鎖:

ContextImpl 中獲取目錄(cache、files、DB 和 preferenceDir)的實現(xiàn)有兩個關鍵耗時點:1. 存在 IPC(IStorageManager.mkdir)和文件 check;2. 加鎖“nSync”保護;所以 ipc 變長和并發(fā)存在,都可能導致 App 卡頓,如圖為 Anr 數(shù)據:圖片圖片圖片

相關的常用 Api 有 getExternalCacheDir、getCacheDir、getFilesDir、getTheme 等,考慮到系統(tǒng)的部分目錄一般不會發(fā)生變化,所以我們可以對一些不會變化的目錄進行 cache 處理,減少帶 鎖方法塊的執(zhí)行,從而有效的繞過鎖等待。

MessageQueue:

Android 子線程與主線程通訊的通用方式是向主線程 MessageQueue 中插入一個任務(message),等此任務(message)被主線程 Looper 調度執(zhí)行;所以 MessageQueue 中會對消息鏈表的修改加鎖保護,主要實現(xiàn)在 enqueueMessage 和 next 兩個方法中。

利用 Slardar 采集線上鎖信息,根據這些信息,我們可以輕松追蹤鎖的執(zhí)有線程和 owner,最后根據情況將請求(message)移到子線程,這樣就可以極大的減輕主線程壓力和等鎖的可能性。此問題的修改方式并不復雜,重點在于如何監(jiān)控到這些執(zhí)鎖線程。

圖片序列化和反序列化:

抖音中有一些常用數(shù)據對象使用 Json 格式存儲。為保證這些數(shù)據的完整性,在讀取和存儲時加了鎖保護,從而導致鎖等待比較常見,這種情況在啟動場景特別明顯;所以要想減少鎖等待,就必段加快序列化和反序列化,針對這個問題,我們做了三個優(yōu)化方案:

  • Gson 反序列化的耗時集中在 TypeAdapter 的構建,此過程利用反射創(chuàng)建 Filed 和 name(key)的映射表;所以我們在編譯時針對數(shù)據類創(chuàng)建對應的 TypeAdapter,大大減少反序列化的時耗。
  • 部分類使用 parcel 序列化和反序列化,大大提高了速度,約減少 90%的時耗。
  • 大對像根據情況拆分成多個小對像,這樣可以減少鎖粒度,也就減少了鎖等待。以上方案在抖音項目中都有使用,取得了很不錯的收益。

AssetManager 鎖:

獲取 string、size、color 或 xml 等資源的最終實現(xiàn)基本都封裝在 AssertManager 中,為了保證數(shù)據的正確性,加了鎖(對象 AssetManager)保護,大致的調用關系如圖:圖片圖片

常用的調用點有:

  • View 構造方法中調用 context.obtainStyledAttributes(...)獲取 TypedArray,最后都會調用 AssetManager 的帶鎖方法。
  • View 的 toString 也調用了 AssetManager 的帶鎖方法。

隨著 xml 異步 inflate 的增加,這些方法并發(fā)調用也增加,造成主線程的鎖等待也日漸突出,最終導致卡頓,針對這個問題,目前我們的優(yōu)化方案主要有:

  • 去掉多余的調用,比如 View 的 toString,這個常見于日志打印。
  • 一個 Context 根據線程名提供不同的 AssetManager,繞過 AssetManager 對象鎖;此方法可能帶來一些內存消耗。

So 加載鎖優(yōu)化:

Android 提供的加載 so 的接口實現(xiàn)都在封裝在 Runtime 中,比如常用的 loadLibrary0 和 load0,如圖 1 和 l 圖 2 所示,此方法是加了鎖的,如果并發(fā)加載 so 就會造成鎖等待。通過 Slardar 的監(jiān)控數(shù)據,我們驗證了這個問題,同時也有一些意外收獲,比如平臺可能有自己的 so 需要加:圖片圖片圖片

我們根據 so 的不同情況,主要有以下優(yōu)化思路:

  • 對于 cinit 加載的 so,我們可以提前在子線程中加載一下 cinit 的宿主類。
  • 業(yè)務層面的 so, 可以統(tǒng)一在子線程中進行提前加載。
  • 使用 load0 替代 loadLibrary0,可以減少鎖中拼接 so 路徑的時耗。
  • so 文件加載優(yōu)化,比如 JNI_OnLoad。

ActivityThread:

在收集的的數(shù)據中我們也發(fā)現(xiàn)了一些系統(tǒng)層的框架鎖,比如下圖這個:

圖片

這個問題主要集中在啟動階段,ams 會發(fā) trim 通知給 ActivityThread 中的 ApplicationThread,收到通知后會向 Choreographer 的 commit 列表(此任務列表不作展開)中添加一個 trim 任務,也就是在下個 vsync 到達時被執(zhí)行;

trim 過程主要包括收集 Applicatioin、Activity、Service、Provider 和向它們發(fā)送 trim 消息,也是系統(tǒng)提供給業(yè)務清理自身內存的一個時機;收集過程是加鎖(ResourcesManager)保護的,如圖:

圖片圖片圖片

考慮到啟動階段并不太關心內存的釋放,所以可以嘗試在啟動階段,比如 40 秒內,不執(zhí)行 trim 操作;具體的實現(xiàn)是這樣,首先替換 Choreographer 的 FrameHandler, 這樣就能接管 vsync 的 doFrame 操作,在啟動 40 秒內的每次 vsync 主動 check 或刪除 commint 任務列表中的 trim 操作。

圖片

收益

在抖音中我們除了優(yōu)化前面列出的這些典型鎖外,還優(yōu)化了一些業(yè)務本身的鎖,部分已經通過線上實驗驗證了收益,也有一些還在嘗試實驗中;通過對實驗中各指標的分析,也證實了鎖優(yōu)化能帶來啟動和流暢度等技術收益,間接帶來了不錯的業(yè)務收益,這也堅定了我們在這個方向上的繼續(xù)探索和深化。

小結

前面列出的只是有代表性的一些通用 Java 鎖,在實際開發(fā)中遇到的遠比這多,但不管什么樣的鎖,都可以根據進程和代碼歸屬分為以下四類:業(yè)務鎖、依賴庫鎖、框架鎖和系統(tǒng)鎖;

不同類型的鎖優(yōu)化思路也會不一樣,部分方案可以復用,部分只能 case-by-case 解決,具體的優(yōu)化方案有:減少調用、繞過調用、使用讀寫鎖和無鎖等。

圖片

分類

描述

進程

代碼

優(yōu)化方案

業(yè)務鎖

源碼可見,可以直接修改;比如前面的序列化優(yōu)化。

App 進程

包含

直接優(yōu)化;靜態(tài) aop

依賴庫鎖

包含編譯產物,可以修改產物

App 進程

包含

直接優(yōu)化;靜態(tài) aop

框架鎖

運行時加載,同時存在兼容性;比如前面提到的 inflate 鎖、AssetManager 鎖和 MessageQueue 鎖

App 進程

不包含

減少調用;動態(tài) aop

系統(tǒng)鎖

系統(tǒng)為 App 提供的服務和資源,App 間存在競爭,所以服務層需要加鎖保護,比如 IPC、文件系統(tǒng)和數(shù)據庫等

服務進程

不包含

減少調用

總結

經過了長達半年的探索和優(yōu)化,此方案已在線上使用,作為我們日常防劣化和主動優(yōu)化的輸入工具,我們評判的點主要有以下四個:

  • 穩(wěn)定性:線上開啟后,ANR、Crash 和 OOM 和大盤一致。
  • 準確性:從目前線上的消費數(shù)據來看,這個值達到了 99%。
  • 擴展性:業(yè)務可以根據場景開啟和關閉采集功能,也可以收集指定時間內的鎖,比如啟動階段可以收集 32ms 的鎖,其它階段收集 16ms 的鎖。
  • 劣化影響:從線上實驗數(shù)據看,一定量(UV)的情況下,業(yè)務和性能(丟幀和啟動)無顯著劣化。

此方案雖然只能監(jiān)控 synchronized 鎖,像 CAS、Native 鎖、sleep 和 wait 都無法監(jiān)控,但在我們日常開發(fā)中synchronized 鎖占比非常大, 所以基本滿足了我們絕大部分的需求,當然,我們也在持續(xù)探索其它鎖的監(jiān)控和驗證其價值。

責任編輯:未麗燕 來源: 字節(jié)跳動技術團隊
相關推薦

2022-03-29 13:27:22

Android優(yōu)化APP

2022-04-28 15:07:41

抖音內存泄漏Android

2024-06-13 17:10:16

2022-06-06 12:19:08

抖音功耗優(yōu)化Android 應用

2019-07-25 13:22:43

AndroidAPK文件優(yōu)化

2022-06-01 09:18:37

抖音ReDex算法優(yōu)化

2013-12-17 16:21:17

iOSiOS性能優(yōu)化

2023-11-03 17:02:18

抖音直播畫質優(yōu)化

2021-07-29 14:20:34

網絡優(yōu)化移動互聯(lián)網數(shù)據存儲

2023-03-03 15:43:23

抖音世界杯畫質優(yōu)化

2019-12-13 10:25:08

Android性能優(yōu)化啟動優(yōu)化

2021-11-09 09:57:46

Webpack 前端分包優(yōu)化

2013-09-16 15:16:20

Android性能優(yōu)化

2013-09-17 10:32:08

Android性能優(yōu)化數(shù)據庫

2021-09-03 09:44:13

移動端性能優(yōu)化U-APM

2013-02-20 14:32:37

Android開發(fā)性能

2025-06-03 00:00:06

性能優(yōu)化性能指標響應時間

2024-11-13 08:47:24

2017-03-14 18:48:06

Android性能優(yōu)化內存優(yōu)化

2017-01-15 15:13:37

Android性能優(yōu)化優(yōu)化點
點贊
收藏

51CTO技術棧公眾號