特效視頻製作軟件app(特效怎麽做)

特效視頻製作(zuò)軟件app(特效怎麽做(zuò))
西瓜視頻穩定性治理體係建設二:Raphael 原理及實踐

摘要

Raphael [1]是(shì)西瓜視頻基礎技(jì)術團隊開發(fā)的一款 native 內存(cún)泄漏檢測工具(jù),廣泛用於字節(jiē)跳動(dòng)旗下各大 App 的 native 內存泄漏治理,收益顯著。工具現已開源(yuán),本文將通過原理、方案和實踐來剖析 Raphael 的相關細節(jiē)。

背景

androids 平台上的內存問題一直(zhí)是性能優化和(hé)穩定性治理的焦點和痛點,Java 堆內(nèi)存因為有比較(jiào)成熟的工具和方法論,加上 hprof 快照作為補充,定位和治理都很方便。而 native 內存問題一直缺乏(fá)穩定、高(gāo)效的工具,僅有的(de) malloc debug [6]不僅性能和穩定(dìng)性難以(yǐ)滿足需要,還存在 androids 版本兼(jiān)容的問題。

現狀

事實上(shàng),native 內存泄漏治理一直不乏優秀的工具,已知的可用(yòng)於調查 native 內存(cún)泄漏問(wèn)題的工具主要有:LeakTracer、MTrace、MemWatch、Valgrind-memcheck、TCMalloc、LeakSanitizer 等。但由於 androids 平台的特殊性,這些工(gōng)具要麽不兼(jiān)容,要麽接入(rù)成本過高,很難在 androids 平台上落地。這些工具的原理基本都是:先(xiān)代理內存分配/釋放相關的函數(如(rú):malloc/calloc/realloc/memalign/free),再通過 unwind 回溯調用堆(duī)棧,最後借助緩存管理過濾出未釋放的內存分配記錄。因(yīn)此(cǐ),這些工(gōng)具的主要差異也就體現在代理實現、棧回溯和(hé)緩存管理三個方(fāng)麵。根據這些工具代理實現的差異,大致可(kě)以分為 hook 和 LD_PRELOAD 兩大類,典型的如 malloc debug [5] 和 LeakTracer。

malloc debug

malloc debug 是 androids 係統自帶的內存調試工具(官方 Native 內(nèi)存調試 有相關介紹 ) ,雖然沒有額外的接入代碼,但開啟方式和核心功能等都受 androids 版本限(xiàn)製。

芭乐视频网页版在線下嚐試使用 malloc debug 監控(kòng)西瓜視頻 App(配置 wrap.sh)時發現,正常啟動時間(jiān)小於 1s 的機型(Pixel 2 & androids 10),其冷啟動時間被拉長(zhǎng)到了 11s+。而且在正常使用過程中滑動時的卡頓感(gǎn)非常明顯,頁麵切換時耗時難以接受,監控過程(chéng)中應(yīng)用的使用體驗極差。不僅(jǐn)如此,西瓜視頻在 malloc debug 監(jiān)控過程中還會遇到必現的棧回溯 crash(堆棧如下,《libunwind llvm 編年史》[8] 有相關分析)。

LeakTracer

LeakTracer 是(shì)另一個比較知名的內存泄(xiè)漏監控工具,其原理是:通過 LD_PRELOAD 機製搶先加載一個定義了 malloc/calloc/realloc/memalign/free 等同名函數的代理庫,這(zhè)樣就全局代理了應用層內存的分配和釋放,通過 unwind 回溯調(diào)用棧並過濾出疑似的內存泄漏信息。androids 平台上的 LD_PRELOAD 是被嚴格限製的,因為其沒有獨立的 unwind 實現,依賴係統的 unwind 能力,也會遇(yù)到 malloc debug 遇到的棧幀兼容問題;如果(guǒ)把 LeakTracer 集成到目標 so 裏通過 override 方式實現代理,隻能攔截到本 so 裏顯式的內存分配/釋放,無法攔截到其他 so 和跨 so 調用的內存分配/釋放。通過 native 插(chā)樁的方式也是如此,隻能監控局部單純的內存泄漏,無法全局監控內存(cún)使用(yòng)。

綜合以上分析和接入體驗,芭乐视频网页版不難發現(xiàn),這些內存泄漏監控工具在 androids 平台上實際接入時基本都存在以下三個(gè)比較典型的問題:

流程繁瑣(suǒ):需要配置 wrap.sh/root permission/setprop 等,受 androids 版本(běn)限製兼容問題:unwind 庫存在嚴重的兼容性問題(tí),libunwind_llvm 無法正確回溯 GNU 編(biān)譯(yì)的棧幀性能問題:官方的 malloc debug 性能數據是損失 10 倍以上,實測(cè)西瓜開啟後在中高端(duān)機上不(bú)可用芭乐视频网页版的需求

西瓜視頻 App 是一個匯集了(le)視頻播放(fàng)、特效拍攝、視頻(pín)剪輯輯、P2P 加速(sù)等 native 代碼非常多的中大型應(yīng)用,每個 native 代碼相關的模塊(kuài)背後都有一個專業團隊在高速迭代,加上日人均使用時長超過 100 分鍾的影響,西瓜視頻 App 的 native 內存問題(tí)治理難度非常大。事實上,單純的內存泄(xiè)漏問題相對較少,更多的是因為業務邏輯不合理(lǐ)帶來的內存使用問題(tí),需要工具滲透到 App 運行的過程中進行監控(kòng),無形(xíng)中提高了對工具性能和穩定性的要求。

線上 native 內存問題基本都是以虛擬內存觸頂的形式暴露(lù)出來的。在西瓜視頻 App 裏,虛擬內存的消耗除了上述幾(jǐ)大模塊外,還有其(qí)他幾個消耗大戶,如線程、webview、Flutter、硬件加速、顯存等。事實上,malloc/calloc/realloc/memalign 等相對於 mmap/mmap64 直接分配出的內存在整個虛擬內存空間中通常占比比較小。因為內存問(wèn)題通(tōng)常以虛擬內存耗盡(jìn)的形式(shì)表現出來,隻有盡可能多的收集各(gè)種內存消耗(hào)來無限逼近虛擬內存上限,才能準確找(zhǎo)出虛擬內存耗盡的原因(yīn)。因此,像 malloc debug 這樣隻監控 malloc/calloc/realloc/memalign/free 等根本無(wú)法滿足內存治理需要(yào),覆蓋 mmap/mmap64/munmap 等盡可能多的內存分配形(xíng)式(shì)是(shì)監(jiān)控工具必須要做的。

綜合上麵的分析可以得出,西瓜視頻 App 乃至整個字節跳動旗下其他 App, 對於一個通用(yòng)的 native 內存泄漏監(jiān)控工(gōng)具的訴求主要(yào)有以下幾個方麵:

接入(rù)層麵:不依賴 androids 版本,無需 root,對業務滲透(tòu)盡可能(néng)低穩定性:不存在影響業務的穩定性問題,可以滿足線上使用的(de)訴求性(xìng)能層麵:沒有明(míng)顯的(de)性能問題,達到可線(xiàn)上使用的標準監控範圍(wéi):不(bú)局限(xiàn)於 malloc/calloc/realloc/memalign/free,至少還能覆蓋 mmap/mmap64/munmapRaphael 核心設計

通過前麵的分析可以(yǐ)知道,一個完整的 native 內存泄漏監(jiān)控工具主要包(bāo)含三部分:代理實現、棧回溯和緩存管理。代理實現是解決 androids 平台上接入問題的關鍵,棧回溯是性能和穩定(dìng)性(xìng)的核(hé)心,緩存邏(luó)輯在一定程度上也會(huì)直接影響性能和穩定性。接下來芭乐视频网页版會從四(sì)個方麵介紹 Raphael 的核心設計。

代理實現

鑒於 wrap.sh 和 LD_PRELOAD 在(zài) androids 平台上不具有(yǒu)通用性,首先被排(pái)除。又因 malloc hook 隻能代理 malloc/calloc/realloc/free,無法覆蓋 mmap/mmap64/munmap,也被放棄。但受 malloc hook 實現方(fāng)式的啟發,借(jiè)助於 inline hook / PLT hook 工具芭乐视频网页版可以實現(xiàn)同樣的代理效果,這其中比較有代表性(xìng)的(de)工具主要有 androids-Inline-Hook[3] 和 xHook[1]。

xHook 是(shì)比較優秀的 PLT hook 工(gōng)具代(dài)表,其穩(wěn)定性可以(yǐ)達到上線標(biāo)準。因其實現依賴正(zhèng)則(zé),同時 hook 的 so 或函數比(bǐ)較多時,hook 耗時會比較明顯。此外,其原生實現隻能 hook 當前已經加載(zǎi)的 so,對於(yú)未加載的沒做特殊處理,如果(guǒ)用來做(zuò)長時(shí)間的進程級監控,需要解決增量(liàng) so hook 問題。不過這種 hook 方式非常適合做 so 定向監控。

與 PLT hook 原理不同,inline hook 則是在目標函數的頭部直接插入跳轉指令,其 hook 的是最終的函數(shù)實現,不存在增量 so hook 問題,hook 效率高效直接。但 inline hook 在 hook 那些可(kě)能正在執行的函數後,需要掛起相關線程進行指令修正,這個是 inline hook 的痛點,現有 hook 實現很多沒有做指令修複,或者在指令修複時或多或少都存在(zài)一些問題。

Raphael 在早期的驗證版本裏采用 xHook 來實現代理接(jiē)入。後續為了實現長時間進程級監控,以覆蓋更多(duō)的(de)業務場景,Raphael 又通過(guò) androids-Inline-Hook 解決增量 so hook 問題,通過 xHook 實現定向監控。為了(le)進一步提(tí)升工(gōng)具的性能和穩定性, Raphael 內部最新版本已切換到了(le) bytehook(字節跳動自研的 PLT hook 工具,可自動處理增量 so hook 問題)。

棧回溯

定位一個對象或者一段內存通常可(kě)以通過引用/依賴關係,也可以(yǐ)通過創建/分配時的堆棧。Java 堆內存因為有明確(què)的組織形式和清晰(xī)的依賴關係,可以通(tōng)過(guò)依賴關係靜態分(fèn)析內(nèi)存泄漏問題。但 native 堆內存依賴/引用比較(jiào)隱晦,也沒有 Java 堆內存那樣明(míng)確的(de)組織格式,無法通過依賴/引用關係進行靜態分析,隻能通過(guò)分配時的堆棧來(lái)輔助定位。棧回溯(unwind)是 native 層獲取調(diào)用堆棧的通用方式,是 native 內存泄漏監控工(gōng)具不(bú)可或缺的核心,同時也是工具性能和穩定性的瓶頸所在。接下來本(běn)文將從棧回溯工(gōng)具選取、限製棧(zhàn)回溯頻次、減少(shǎo)無用棧回溯三個方麵介紹 Raphael 在棧回溯上所做的工作。

棧(zhàn)回溯工具選取

androids 平台上(shàng)常用的 32 位棧回(huí)溯庫主要有:libunwind_llvm、libunwind (nongnu)、libgcc_s、libudf、libbacktrace、libunwindstack 等,實(shí)踐證實(shí)這些工具或多或(huò)少都(dōu)存在一些問題,以(yǐ)下是芭乐视频网页版基於三個主流的棧(zhàn)回溯庫做的簡(jiǎn)單對比分析(平台:Pixel 2 & androids 10,性能:Demo 裏統(tǒng)計(jì) 16 層棧幀回(huí)溯的總耗時;兼容性:字節跳動旗下(xià)多個應用長時間的優化治理實踐)

棧回(huí)溯涉及到的(de)東西(xī)比較(jiào)多(duō),想要(yào)自己短時間內實(shí)現一個在穩定性、回溯性能、回溯成功率等方麵(miàn)都表現優異的 32 位棧回溯工(gōng)具難度非(fēi)常大。為了快速驗證並解決實際機問題,Raphael 在早期版本裏采用的是 libunwind_llvm,隨後切換到 libunwind_llvm & libunwind (nongnu),通過 libunwind_llvm 保證回溯性能,在回溯深度低於 2 層時切(qiē)換到 libunwind (nongnu),以保證回溯成功率。最新版本裏則采用的是 libudf,兼具了性能和回溯成功率。相對而言,64 位下基於 FP 的棧(zhàn)回溯實現性能和穩定性基本都能滿足需求,這裏不做過多介紹。Rapahel 同(tóng)時也在設計時做了充分(fèn)的(de)擴(kuò)展考慮,可以輕鬆切換到(dào)其他(tā)更優秀的棧回溯實(shí)現。

限製棧回溯頻次

即便是 libudf 實現,其在 demo 裏回溯 16 層棧幀的平均耗(hào)時也需要 0.6ms,監控(kòng)工具實際運行時對 App 性(xìng)能的影響是很明顯的。提(tí)升監控性能的途徑除了直接(jiē)優化棧回溯性能外,減少回溯頻次也是(shì)十分有效(xiào)的手段。芭乐视频网页版(men)在西瓜視頻 App 的優化(huà)治理實踐中發現,多數場景小於 1024 byte 的內(nèi)存(cún)分配其頻率約占 70% 以上,但線上遇到(dào)的 native 內(nèi)存觸頂問題,卻很少(shǎo)是由小內(nèi)存泄漏引發的,監控小內存(cún)泄漏對於解決線(xiàn)上 native 內存觸頂問題沒有實質效果。即便真的是由小內存引(yǐn)發的,這個需要高頻和必現的場景才能達到,這類問題通常在線下單測(定向監控(kòng))場景是完全可以覆蓋到的。基於此,Raphael 通過設定內存閾值(zhí)來控製棧回溯頻次,可(kě)以大幅降低棧回溯的性能損耗。

減少無(wú)用(yòng)棧回溯

受限(xiàn)於代理流程和棧回溯實現機製,從代理函數入口到回溯開始的路徑上會存在幾層跟分配堆棧無關的函數調用,這幾層調用最終會體現在最後回溯成功的堆棧上(下圖的紅色部分),每次內(nèi)存分配都回溯這幾層無用的調用鏈是十分損耗性能的。解決這種問題的直觀方法就是(shì)減少甚至完全規避這種無關的棧回(huí)溯,體現在代(dài)碼層麵就是減少代理入口到回溯開啟函數之間的調用層級。inline 是一種簡單直接的實現方式,也可以直接在(zài)代理入口處提前構建回溯的 context 數(shù)據。

緩存管(guǎn)理

緩存管(guǎn)理作為 native 內存監控的重要一環(huán),對整個監控工(gōng)具性能的影響至關重要。以 malloc debug 和LeakTracer 為例,它們都是(shì)通過分配(pèi)後的內存地址(zhǐ)作為 key 來計算(suàn) hash 後散列存儲的,並通(tōng)過一個全局鎖來同步緩存更新的時序。兩者不同的是,malloc debug 會通過堆棧聚合調用鏈(liàn)完(wán)全相同的內存分配記錄,其(qí)緩存的存儲單(dān)元通過 malloc 動態分配;而 LeakTracer 則不會根據堆棧(zhàn)聚合,其存儲單元會預先分配一部分,緩存不足時也會動態申請。通過以上分析和實測可以發現,malloc debug 的實際性能比(bǐ)LeakTracer 低很多,原因主要體(tǐ)現在堆棧聚合和緩存動態分(fèn)配上。

對比 malloc debug 和 LeakTracer 的源碼(mǎ)也可以發現:運行時的堆棧聚合是(shì)完全沒有必要(yào)的;如(rú)果限製內存監控的閾值,緩存空間和緩存單元的上限(xiàn)都可(kě)以控(kòng)製在一定(dìng)範圍內的,不需要動態(tài)申請,可以減少動態分配的性能損耗;此外,由(yóu)於 native 內存分配和釋放頻(pín)率比較高,全局鎖一定程序上會影響整體性能,通過 key 計算 hash 後再散列存儲時不需要全局(jú)鎖。

Raphael 是預先分配固定大小的緩存空間,除了發生內存觸頂(dǐng)導致的 crash 外,緩存單(dān)元提前耗完也認為(wéi)存在內存泄漏問題(tí)。這主要是因為:對於 32 位進程,其虛擬內存(cún)的上限通常是(shì) 4G,正常運行時相對比較容易觸達上限(xiàn),而(ér) 64 位進程的虛擬地址(zhǐ)空間非(fēi)常大,實際很難遇到虛擬內存觸頂的 case,但遇到物理內存不足的概率則(zé)要大很多,這(zhè)與 32 位進程基本相(xiàng)反。通過控製 vmPeak 閾值和緩存(cún)單(dān)元餘量(liàng)可以有效捕捉到內存(cún)泄漏數據,最終實現穩定可靠的全自動內存泄漏監控及消費(fèi)流程

監控範圍

通過前(qián)麵的分析可以知道,隻監控 malloc/calloc/realloc/memalign/free 是無(wú)法滿足治理需求的,這主要是因為 malloc/calloc/realloc/memalign/free 等分配出的內存通常在整個虛擬(nǐ)內存空間裏占(zhàn)比較小,常見的內存消耗大戶 Thread、webview、Flutter、硬件加速、顯存(cún)等,都不是通(tōng)過這些(xiē)函數分配出的。為了能夠對 androids 平台上的 native 內存觸頂問題精準歸因,監控需要無限逼近虛擬內存的上限,這就需(xū)要監控盡可能(néng)多的(de)內存分配形式。

androids 上的內存操作主要是 malloc/calloc/realloc/memalign/free 和 mmap/mmap64/munmap,同監控 malloc/calloc/realloc/memalign/free 相比(bǐ),監控 mmap/mmap64/munmap 有兩點不同:一個是線(xiàn)程棧的(de)釋放問題(tí),雖然創建線程時是通(tōng)過 mmap/mmap64 分配的棧內存,但棧內存的釋放並(bìng)不一定是通過顯式調用 munmap 實現的;另一個是(shì)監控重(chóng)入問題,當通過 malloc/calloc/realloc/memalign 等分配大內存時,底層通常是通過 mmap/mmap64 實現的,兩類接口同時監控時會存在重(chóng)入(rù)問題。

棧內存釋放

線程(chéng)的棧內存又分為(wéi)信號棧和執行(háng)棧,信號棧在調用void pthread_exit(void *return_value) 接口(kǒu)時(shí)會通過 munmap 即刻釋放(fàng),而執行棧的釋放則有兩種形式:

void pthread_exit(void return_value) 函數體裏,當線程狀態為 THREAD_DETACHED 時會直接通過 void _exit_with_stack_teardown(voidstack, size_t sz) 釋放

int pthread_join(pthread_t t, void** return_value) 裏通過(guò) pthread_internal_remove_and_free,最終在pthread_internal_free 裏通過 munmap 釋放

綜上,最終通過 munmap 釋放的內存都可以被監(jiān)控到,而通過_exit_with_stack_teardown 釋放的內存則無法攔截到。芭乐视频网页版針對這種情況做了特殊處理:在 Raphael 裏代理攔截(jié)了 void pthread_exit(void *) ,並判斷此時線程狀態是否為 THREAD_DETACHED,如果是(shì)則在監控裏直接移(yí)除相關記錄,否則不移除。

重入問題

下圖是一個典(diǎn)型的(de)重入(rù)現場,其上層的(de) malloc 函數最終調用到了 mmap 函數,同(tóng)時監控(kòng)兩(liǎng)類內存接口(kǒu)時就會遇到此(cǐ)類問題。重入問題帶(dài)來的一個挑戰是緩(huǎn)存如何管理,同一個緩存裏隻能維護一個記(jì)錄,維護兩(liǎng)個記錄的邏輯(jí)和(hé)性能過(guò)於複(fù)雜。此外,從 malloc 到 mmap 的堆棧是固定的,這幾層(céng)堆(duī)棧對分析內存泄漏完全沒用,因為這個時(shí)候(hòu)關注的是 malloc 之上的堆棧。

解決重入問題的方案很直接,在檢測到 mmap/mmap64 之上有 malloc/calloc/realloc 等棧(zhàn)幀時,忽略本次分配。這樣不僅解決了重入問題,也(yě)避免了不必要的棧回(huí)溯。因為 androids 平台不支(zhī)持 thread local storage(TLS),隻能通過 pthread_setspecific 和 pthread_getspecific 實現。

綜合評估功能

相對於 malloc debug 和 LeakTracer,Raphael 不僅支持 malloc/calloc/realloc/memalign/free,也支持監控 mmap/mmap64/munmap 等,使監控範圍擴展到(dào)了(le)線程、webview、Flutter、顯存等,基本完全覆蓋了 androids 平台上的 native 內存(cún)使用場(chǎng)景

性能

androids 平台上的 native 內存泄漏檢測通常都是在程序運行(háng)過程中進行的,棧回溯和緩存管理會消耗部分 CPU 和內存(cún),帶來一定的性能損失。Raphael 可配置(zhì)的(de)監控能力有很大的伸縮性(xìng),性能影響可以限製在可接受(shòu)範圍內(nèi),以(yǐ)下數據基於西瓜視頻(pín) App 32 位模式評測(中高(gāo)端機型和(hé) 64 位下的性能更高):

CPU:32 位模式 & ≥1024 的監控閾值下,在低端機上 CPU 消耗< 3%內存:32 位模式下默認會有約 16M 的虛擬內存消耗幀率:32 位模(mó)式 & ≥1024 的監控閾值下,低端機上幀率沒有明顯變化穩定性

已開源的版本是基於開(kāi)源 inline hook 實現的,在部分 androids 6 機型上存在卡死問題,除此之外暫未發現其他穩(wěn)定性問題。此外,字節跳動這邊早(zǎo)期的治理實踐集中在線下,並基於 Raphael 建(jiàn)設完善了線下(xià)的防治體係,更為穩定的版本可以(yǐ)滿足線上的監控需求,芭乐视频网页版會在後續迭代開源。

治理實踐

Raphael 在字節跳動內部使用非常廣泛,是字(zì)節跳動 native 協會指定的 native 內存泄漏檢測工具。在治理實踐中(zhōng),Raphael 覆蓋了幾乎(hū)所有的 native 內(nèi)存使用場景,輔助解決了大量的 native 內存泄漏(lòu)和內存使用不合理的問題(tí)。接下來(lái)通過四(sì)個典型的案例簡單介(jiè)紹下 Raphael 的監控能力和基於 Raphael 的(de)數據(jù)分析方法(應用(yòng)自身的,Java 層的,webview 的,係統層的)

案例 1

下圖是(shì)西瓜視(shì)頻裏兩個比較典型的 native 內存問題(tí)現場,既有嚴格意義上的內存泄漏(用完之後未釋放),也有更為廣泛的內存不(bú)合理使用的問題(短暫(zàn)泄漏、局部場景問題、上層業務邏輯問題等)。針(zhēn)對(duì)內(nèi)存泄漏問題,在明確了相關內存的生(shēng)命周期之後,可(kě)以相對(duì)輕鬆地快速定位到。對於(yú)內(nèi)存使(shǐ)用(yòng)不合理的問題,則需要盡(jìn)可能(néng)多的搜集未(wèi)釋放的內存(cún),來綜合評估影響。

早期在分析數據時(shí),芭乐视频网页版也會通過 maps 來驗證(zhèng) Raphael 的數據。通常通過分析 maps 可以大致知道(dào)內存觸頂的原因,下圖是一個典型的運行時通過 malloc/calloc/realloc/memalign 和 mmap/mmap64 分配的內存過多導致的 OOM 現場。

案(àn)例 2

下圖是字節跳動(dòng)內部一個業務遇(yù)到的 native 內存問題現場,未接入 Raphael 前雖能輕(qīng)鬆複現 native 內存(cún)增長(zhǎng)的問題(tí),但無法定位內存增長的原因。在接(jiē)入 Raphael 後,雖然攔截到的內存並不多(duō),但問題暴露得非常明顯。排(pái)名第一個的堆(duī)棧(zhàn)是 Java 層(céng)創建 bitmap 對(duì)象時調用到 native 層堆棧(androids 8 以後 Bitmap 的數據是存儲在 native 層),該問題的調查最終轉移(yí)到了 Java 層。

基於以上分析,芭乐视频网页版可以斷定 Java 層的堆內存裏(lǐ)一定存在大量的 Bitmap 對象。因為該問題是線下(xià)可複現的,芭乐视频网页版可以很容易(yì)地通過 Java 堆內存快照驗證(zhèng)並定(dìng)位到問題原因(如下圖所示)。如果是線上,芭乐视频网页版需(xū)要抓取異常現(xiàn)場的快照才能最終定(dìng)位,這也正(zhèng)是 西(xī)瓜(guā)視頻穩定性治理體係建設一:Tailor 原理及實踐 裏所(suǒ)提到的通用(yòng)異(yì)常數據搜集建設。

案例 3

一直以來 androids 設備上 webview 消耗的(de)內存很少(shǎo)被重視,隨著前端業(yè)務場(chǎng)景增多,webview 導致的內存問題也越來越明顯、越來越頻繁(fán)。下圖是 Raphael 在西瓜視頻 App 裏(lǐ)監控到的一個前(qián)端活動頁導致的內存問題現場。由於係統 webview 自身的原因,工具無法回溯(sù)出完整的調用棧,無法直觀定位到(dào)問題原因。最終芭乐视频网页版通過定向分析內存數據,定位到(dào)這些內存基本都(dōu)是前(qián)端頁麵裏緩存的圖片資源,在對該頁麵的圖片緩存策略進行優化之後,相關的內存觸頂的異常大幅降低。

案例 4

下圖是 androids 係(xì)統(tǒng)上長期存在的一類 Camera 內存泄漏現場。通(tōng)過分析源碼可知,Camera 在拍攝(shè)過程中會在 native 層持續構造 CameraMetadata 實例,而每個 CameraMetadata 對象都(dōu)會指向一塊不小的 native 內(nèi)存,這塊 native 內存的釋放依賴 Java 層的 CameraMetadataNative 對象執行(háng) finalize 函數。這個邏輯最終導致(zhì)這部分 native 內存的(de)回收間接依賴 Java 層的 GC。如果(guǒ)一段時間內 Java 層沒有 GC ,這部(bù)分 native 內存就會因為沒有及時(shí)釋放而堆積,進(jìn)而在觸頂後引發各(gè)種因 native 內存不足而導致(zhì)的異常。《androids Camera 內存問題剖析》裏有詳細的分析(xī)過程,《ART 視角 | 如何讓 GC 同步回收 native 內存》針對此類問題也同步給出了方案,通(tōng)過溝(gōu)通 androids 團隊表示(shì)會(huì)在後續版本裏徹(chè)底修複此(cǐ)問(wèn)題。

後續規劃

Native 內存泄漏監控的原理相對簡單,但想要做(zuò)到完美通(tōng)用卻很困難,最主要的(de)考驗當(dāng)屬性能和穩定性問題,例如(rú) 32 位棧回溯的性能和穩定性、緩存管理的性能等。前期(qī)芭乐视频网页版在(zài)調(diào)研和(hé)開發 Raphael 時(shí),基於快速落地和解(jiě)決緊迫問(wèn)題的目的,複用了大量第三方代碼,並簡化了很(hěn)多邏輯。經(jīng)過長期(qī)的治理實(shí)踐,工具自身也暴露出一些問題和後(hòu)續可以優化的方向。

就代理邏輯而言,androids-Inline-Hook 和 And64InlineHook 雖然(rán)都是比較優秀的 inline hook 工具,但實際使(shǐ)用時仍然存在兼容(róng)和卡死的問題。雖然 xHook 在(zài)兼容性和性(xìng)能(néng)上都(dōu)可以達到上線標準,但不具有通用性,很難將 native 內存泄漏監(jiān)控擴展到其他有上限的資源上(如 JNI Reference Table)。芭乐视频网页版也在調(diào)研優化 inline hook,探索更為穩定高效(xiào)的 hook 方案。

棧回溯和緩存管理是 native 內存泄漏監控性能和(hé)穩定性的(de)瓶頸。相對而言,基於 FP 的 64 位棧回溯方案已經到了極致,但 32 位下目前仍沒有完美理想的方案。在 32 位下,Raphael 通過限製棧回溯深度和控製監控範圍來規(guī)避頻繁棧回溯帶來的性能影(yǐng)響,雖然可以大幅提升性能,但(dàn)也存在(zài)漏報問(wèn)題。因此,32 位棧(zhàn)回溯性能也是芭乐视频网页版後(hòu)續的優(yōu)化方(fāng)向。此外,Raphael 已開源的版本其緩存管理(lǐ)仍然是通過全局鎖來實現同步的(de),會有一(yī)定的性能損失,這個芭乐视频网页版也會在(zài)後(hòu)續的開源迭代(dài)裏同(tóng)步最新的優化。

眾所周知,物理內存(cún)、虛擬內存、Thread、FD、JNI Reference Table 等(děng)都(dōu)是典型的有上限的資源,不合(hé)理使用都會造(zào)成常規手段難以(yǐ)調查的穩定性問題。顯而易見,內存泄漏的監控邏輯(jí), 同樣適用於其他(tā)這些有上限的資源。甚至於那些雖然沒有明(míng)確上限的(如 Binder、流量、耗時等),芭乐视频网页版也可以構造出相應的(de)上限來實現監控和溯源。基於 Raphael 擴展(zhǎn)其他的監控能力是芭乐视频网页版後續要高優完(wán)善的。

總結

androids native 內存泄(xiè)漏話題由來已久,在此之前(qián)業界一直沒有穩(wěn)定可靠(kào)的工具可用,得益於 AOSP 和其他優秀(xiù)的開源項目(androids-Inline-Hook、And64InlineHook、xHook、xDL),使得芭乐视频网页版(men)有(yǒu)機會進行相關的嚐試。Raphael 是(shì)西瓜視頻基礎技術團隊(duì)的初步(bù)探(tàn)索(suǒ)和嚐試(shì),在字節跳動(dòng)內部眾多 App (如西瓜(guā)、抖音、頭條(tiáo))長期的治理實踐中,不僅解決了大量疑(yí)難問(wèn)題,也進一(yī)步完(wán)善(shàn)了工具和方法論。

雖然基於 Raphael 的 native 內存(cún)泄漏監控(kòng)方案目前已(yǐ)經足夠成熟和穩定,但其監控過程畢竟滲透到了 App 的運行過程,會有一定程度的性能損失和穩定性風險。芭乐视频网页版倡導的方案是基於此來建設(shè)完善線下的內存泄漏防治體係,謹慎帶到線上。由於內部迭代的 Raphael 版本比較多,且涉及其他未(wèi)開源的項目,本次開源芭乐视频网页版隻能選擇(zé)其中(zhōng)一(yī)個穩定可用的版本,其他優化會在後續逐步開源。

Raphael 隻是邁開了其中(zhōng)的一小(xiǎo)步,方案還有很大的優化空間。開源不是終點,芭乐视频网页版希望集思廣益、共同探索完善,在 androids 穩定性治理(lǐ)上走(zǒu)得更快更遠(yuǎn)。

相關資料Raphael 開源地(dì)址:https://github.com/bytedance/memory-leak-detectorxHook 鏈接:https://github.com/iqiyi/xHookxDL 鏈接:https://github.com/hexhacking/xDLandroids-Inline-Hook 鏈接:https://github.com/ele7enxxh/androids-Inline-HookAnd64InlineHook 鏈接(jiē):https://github.com/Rprop/And64InlineHookmalloc debug 鏈接:https://androids.googlesource.com/platform/bionic/+/master/libc/malloc_debug/README.mdLeakTracer 鏈接:http://www.andreasen.org/LeakTracer/ androids Camera 內存問題剖析 libunwind llvm 編年史:https://zhuanlan.zhihu.com/p/33937283ART 視角 | 如何讓 GC 同步回收 native 內存:https://juejin.cn/post/6894153239907237902


加入芭乐视频网页版

歡迎加入字節跳動西瓜視頻客戶端團隊,我(wǒ)們專注於西瓜視頻 App 的開發和基礎技術建設(shè),在客戶端架構、性(xìng)能、穩定性、編譯(yì)構(gòu)建、研發工具等方向都有(yǒu)投入。如果你(nǐ)也想一起攻克技術難題,迎接(jiē)更大的技術挑戰,歡迎(yíng)加入(rù)芭乐视频网页版 !

西瓜視頻客戶端團隊(duì)正在熱招 androids、ioses 架構師和研發工程師,最 Nice 的工作氛圍(wéi)和成長機會,各種福利各種機遇,在北京、杭州、上海三地均有職位,歡迎投遞簡曆!聯(lián)係郵箱:tech@bytedance.com ;郵件標題:姓(xìng)名-工作(zuò)年限-西瓜-androids/ioses/基礎技術。

歡迎關注「字節跳動技術團隊」

網址格式不正確

網址格式不正確

申明:如本(běn)站文章或轉(zhuǎn)稿涉及版權等問題,請您及時聯係本站,芭乐视频网页版會盡快處理!

上一篇:內存漲價最新消息(內存漲價2021年6月)

下一篇(piān):定級賽是什麽(me)意思-(定(dìng)級賽秒了算(suàn)輸嗎)

返回
13617680498
  • 微(wēi)信號:13617680498微信二(èr)維碼(mǎ)

网站地图 芭乐视频网页版_芭乐APP最新下载网址进入安卓_芭乐视频在线网_芭乐视频黄在线观看