<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Jack Jiang

    我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
    posts - 494, comments - 13, trackbacks - 0, articles - 1

    本文由QQ技術(shù)團(tuán)隊分享,本文收錄時有內(nèi)容修訂和大量排版優(yōu)化。

    1、引言

    QQ 作為國民級應(yīng)用,從互聯(lián)網(wǎng)興起就一直陪伴著大家,是很多用戶剛接觸互聯(lián)網(wǎng)就開始使用的應(yīng)用。

    而 QQ 桌面版最近一次技術(shù)架構(gòu)升級還是在移動互聯(lián)網(wǎng)興起之前,在多年迭代過程中,QQ 桌面版也積累了不少技術(shù)債務(wù),隨著業(yè)務(wù)的發(fā)展和技術(shù)的進(jìn)步,當(dāng)前的架構(gòu)已經(jīng)無法很好支撐對 QQ 的發(fā)展了。

    在 2022 年初,我們下定決心對 QQ 進(jìn)行全面的技術(shù)架構(gòu)升級,對于這樣一個國民級應(yīng)用的重構(gòu),挑戰(zhàn)無疑是巨大的。

    新版桌面 QQ 自內(nèi)測以來也受到許多熱心網(wǎng)友和行業(yè)人士的關(guān)注,非常感謝大家在內(nèi)測過程中提的各種有建設(shè)性的建議和反饋。其中,也有一小部分有開發(fā)背景的用戶對我們采用 Electron 框架表達(dá)擔(dān)心:高內(nèi)存占用、超大安裝包、啟動緩慢等。究其原因還是擔(dān)心新版本 QQ 資源占用大、體驗(yàn)變差,針對用戶的擔(dān)心,我們在內(nèi)存上進(jìn)行了專項優(yōu)化,也取得了一些階段性的進(jìn)展,過程中也積累了不少經(jīng)驗(yàn),也借此機(jī)會分享給大家。

    本文我們將和大家分享新版 QQ 在內(nèi)存優(yōu)化方面的探索和階段性優(yōu)化進(jìn)展。雖然本文的討論主要集中在 Windows 平臺,但由于 Electron 的跨平臺特性,大部分優(yōu)化措施也同樣適用于 macOS 和 Linux 平臺。

     
    技術(shù)交流:

    (本文已同步發(fā)布于:http://www.52im.net/thread-4429-1-1.html

    2、系列文章

    本文是系列文章中的第9篇,本系列總目錄如下:

    3、新版 QQ 在內(nèi)存上的挑戰(zhàn)

    新版 QQ 在內(nèi)存上的挑戰(zhàn)主要表現(xiàn)在以下 4 個方面:

    1)產(chǎn)品形態(tài):

    由 1 個復(fù)雜的大面板(100+ 復(fù)雜程度不等的模塊)和一系列獨(dú)立功能窗口構(gòu)成。窗口與渲染進(jìn)程一一對應(yīng),窗口進(jìn)程數(shù)很大程度影響 Electron 的內(nèi)存占用。

    對于那個復(fù)雜的大面板, 一旦沒有精細(xì)控制就很容易導(dǎo)致內(nèi)存持續(xù)走高。

    ▲ Electron 窗口多進(jìn)程示意

    2)使用習(xí)慣:

    用戶長時間掛機(jī)。相比用完即走的 Web 頁面,QQ 用戶在一次登錄后,可能會掛機(jī)一個月以上。這段期間,如果沒有控制好 QQ 內(nèi)存使用,那么結(jié)果可能是內(nèi)存越占越大、用戶交互響應(yīng)變慢、甚至發(fā)生閃退。

    3)版本迭代:

    已經(jīng) 24 歲的 QQ 擁有眾多的功能和特性。

    過去一年我們一直做這件事——從核心特性開始快速補(bǔ)齊 Windows 版本的功能,同時也有一些高優(yōu)先級的新功能要上。持續(xù)且快速的版本迭代,很可能產(chǎn)生新問題,使性能劣化。

    4)應(yīng)用架構(gòu):

    新版 QQ 依賴一個 NT 核心數(shù)據(jù)模塊(C++ addon),為 UI 提供本地化的數(shù)據(jù)服務(wù)。QQ 的加載體驗(yàn)?zāi)茏龅饺绱私z滑,這個模塊起到了至關(guān)重要的作用。

    同時,與 NT 的聯(lián)動優(yōu)化,也需要拉通客戶端 C++ 開發(fā)同學(xué)共同完成。當(dāng)然,會存在一些溝通成本,但不可否認(rèn),能把內(nèi)存占用壓下來,客戶端同學(xué)也付出了非常多的努力。

    ▲ 新桌面端 QQ 整體架構(gòu)

    4、新版 QQ 的內(nèi)存現(xiàn)狀與優(yōu)化目標(biāo)

    在著手優(yōu)化之前,我們結(jié)合舊版 QQ 以及其他優(yōu)秀的桌面應(yīng)用,給新版 QQ 設(shè)定了優(yōu)化目標(biāo):

    • 1)第一階段目標(biāo):單個進(jìn)程內(nèi)存 < 300M。
    • 2)第二階段目標(biāo):單進(jìn)程 <100M,整體 < 300M。

    針對第1)階段目標(biāo):早先因?yàn)闆]有騰出手處理內(nèi)存問題,代碼中存在一些泄漏。長時間掛機(jī)后比較容易出現(xiàn)單個進(jìn)程超過 300M 的情況。我們在去年 9 月份系統(tǒng)地處理過一波內(nèi)存問題,基本可以保證單個進(jìn)程的內(nèi)存占用 < 300M。

    針對第2)階段目標(biāo):整體是指啟動 QQ 聊天面板后,6 個進(jìn)程內(nèi)存占用之和。內(nèi)存達(dá)標(biāo)之后才允許交付新版 QQ Windows 版本。

    ▲ Windows 任務(wù)管理器的 QQ 內(nèi)存占用詳情

    這些進(jìn)程會隨著 QQ 的啟動一直存在。我們重點(diǎn)看下這 3 類進(jìn)程,這也是內(nèi)存優(yōu)化的大頭:

    • 1)node:Electron 的主進(jìn)程,負(fù)責(zé)窗口管理、跨進(jìn)程通信等。包含 NT 核心數(shù)據(jù)模塊,負(fù)責(zé)與服務(wù)端交互,為 UI 提供數(shù)據(jù)服務(wù);
    • 2)renderer:Chromium 內(nèi)核的渲染進(jìn)程,負(fù)責(zé)渲染 UI、提供用戶交互等。QQ 啟動后,會有 2 個渲染進(jìn)程:一個是 QQ 大面板,另一個是主進(jìn)程的窗口池。窗口池是預(yù)創(chuàng)建的一個渲染進(jìn)程。在新開窗口時,可以減少等待時間;
    • 3)gpu:Chromium 內(nèi)核的 GPU 進(jìn)程。它的主要作用是處理與圖形相關(guān)的任務(wù),例如渲染網(wǎng)頁、播放視頻、執(zhí)行動畫等。

    設(shè)定了目標(biāo)后:我們先對 QQ 的內(nèi)存占用情況進(jìn)行了摸底。我們從用戶的角度出發(fā),使用 Windows 任務(wù)管理器來觀察 QQ 的內(nèi)存占用情況。我們先從最簡單的 “Hello World” 開始,看看 Electron 應(yīng)用的最低內(nèi)存需求是多少,以及上限在哪里。結(jié)果顯示,只需要 68M,并沒有達(dá)到傳說中的幾百 M 那么大。

    然而:隨著使用的深入,比如在 QQ 聊天場景中進(jìn)行一些操作之后,主進(jìn)程、GPU 進(jìn)程和渲染進(jìn)程三個進(jìn)程的內(nèi)存占用就已經(jīng)達(dá)到了 600M。這意味著我們距離目標(biāo)還有超過 50% 的優(yōu)化空間。

    (注:AIO 是聊天面板的簡稱)

    這個初步的觀察讓我們看到了目前的挑戰(zhàn),同時也讓我們看到了優(yōu)化的可能性。我們有信心,通過精心設(shè)計和持續(xù)優(yōu)化,逐步接近甚至超越我們設(shè)定的目標(biāo)。

    5、內(nèi)存優(yōu)化我們都做了什么

    接下來,將重點(diǎn)介紹我們是如何掌控和優(yōu)化 Electron 的內(nèi)存的。

    我們的工作主要包括以下幾個方面。

    1)工具分析:首先,我們需要使用不同維度的內(nèi)存分析工具,從 V8 引擎到進(jìn)程,再到整個應(yīng)用程序,打通整個鏈路進(jìn)行多角度的細(xì)節(jié)分析,以此來定位內(nèi)存使用的瓶頸。

    2)定向優(yōu)化:在通過工具定位到問題之后,我們會采取一系列的針對性優(yōu)化策略,包括緩存策略、按需加載、優(yōu)雅降級等。具體的優(yōu)化工作我們將在后面進(jìn)行詳細(xì)介紹。

    3)線上監(jiān)控:在本地或小范圍內(nèi)驗(yàn)證通過之后,我們需要廣大用戶的驗(yàn)證來確認(rèn)我們的優(yōu)化措施是否適用于所有場景。然而,如何獲取用戶在 Windows 任務(wù)管理器中看到的內(nèi)存使用量是一個挑戰(zhàn),我們已經(jīng)做了大量的研究和驗(yàn)證。

    4)防止性能退化和自動化測試:為了保護(hù)我們辛苦得來的優(yōu)化成果,并避免頻繁的版本迭代影響 QQ 的內(nèi)存目標(biāo),我們會借助開發(fā)框架、工具建設(shè)、代碼審查等手段來預(yù)防性能退化。

    6、選擇合適的分析工具

    在進(jìn)行性能優(yōu)化之前,我們需要選擇合適的工具來幫助我們分析問題。

    QQ 的代碼不僅包含 V8 的 JS 部分,還包括許多 Native 的 C++ 模塊。僅依靠 Chromium 開發(fā)者工具進(jìn)行性能分析是不夠的,因此我們需要組合使用多種工具來共同解決問題。

    這些工具如何使用,由于篇幅的關(guān)系我們在這里不做詳細(xì)介紹。

    ▲ 部分內(nèi)存分析工具截圖

    7、定向優(yōu)化1:最大化資源使用率

    7.1代碼及靜態(tài)資源

    桌面版 QQ 的功能邏輯非常復(fù)雜,代碼量龐大。雖然代碼不需要通過網(wǎng)絡(luò)請求加載,本地加載速度通常較快,但加載如此龐大的代碼會占用大量內(nèi)存。

    因此,仍然需要進(jìn)行代碼瘦身、靜態(tài)資源優(yōu)化、分包和按需加載等優(yōu)化措施。

    ▲ Devtools > Memory 分析 QQ 主窗口內(nèi)存占用

    首先是代碼瘦身:對于第三方包或 SDK,它們往往包含了完備的 Web 兼容性及能力,而這些對于 Electron 客戶端來說并不是必需的。因此,我們會對它們進(jìn)行定制裁剪或獨(dú)立實(shí)現(xiàn),以減少代碼的加載。

    對于 QQ 的業(yè)務(wù)代碼:分包策略不完全按照每個頁面(窗口)以及模塊復(fù)用次數(shù)來進(jìn)行制訂,更多的情況是按照場景模塊來進(jìn)行細(xì)粒度的定制。

    以打開一個窗口到進(jìn)入使用場景為例:

    • 1)窗口池中預(yù)啟動的窗口頁面只加載必須執(zhí)行的基礎(chǔ)代碼;
    • 2)當(dāng)打開具體窗口時加載對應(yīng)的路由后頁面入口代碼;
    • 3)當(dāng)具體使用不同功能時動態(tài)加載(如點(diǎn)擊搜索、打開表情面板、轉(zhuǎn)發(fā)消息激活好友選擇器的時候才會分別加載對應(yīng)功能模塊代碼)。

     

    ▲ QQ 主窗口業(yè)務(wù)模塊的拆解

    此外:其他靜態(tài)資源(如 SVG、base64 圖像)在加載時也會占用不少內(nèi)存,所以我們采取了按需加載的策略(只在可見時加載,不可見時主動銷毀和回收)。

    ▲ svg 及 base64 資源的 string 內(nèi)存占用

    為了提升執(zhí)行效率和代碼保護(hù)的目的,我們將 JS 代碼轉(zhuǎn)成了字節(jié)碼。盡管跳過了源碼編譯,直接將字節(jié)碼交給 V8 執(zhí)行,但在程序報錯還原堆棧等運(yùn)行時步驟中,V8 仍然會引用源碼字符串。為了去掉這份源碼,我們使用和源碼等長的空格來占位,但通過 devtool 檢查發(fā)現(xiàn)這些空格字符串仍會占用不少內(nèi)存空間。最終,我們采取修改和移除 V8 對源碼字符串引用的方式,徹底解決了源碼字符串的內(nèi)存占用問題。

    7.2圖片資源

    QQ 作為一款 IM 工具,會涉及到大量的圖片收發(fā)。然而,圖片的渲染會占用相當(dāng)大的內(nèi)存。

    舉個例子:一張分辨率為 4000 x 2750 的圖片,結(jié)合設(shè)備屏幕像素和聊天區(qū)設(shè)計尺寸,只需渲染寬度為 567 像素的分辨率圖像即可清晰展示。如果以寬度為 4000 像素的分辨率渲染,理論上兩者位圖所占用的內(nèi)存大小差距可達(dá) 50 倍,并且還會因?yàn)殇秩編硇阅軗p失。

    ▲ 圖片尺寸對內(nèi)存影響舉例

    在聊天消息列表中的大部分圖片僅僅起到預(yù)覽作用,縮略圖渲染就滿足了需要。而僅僅在用戶真正打開圖片查看器放大查看時,才會需要用原圖渲染。

    實(shí)測在聊天中多張不同大尺寸分辨率圖片在展示時,渲染進(jìn)程和 GPU 進(jìn)程的內(nèi)存占用有著明顯差別。在收發(fā)圖片時,我們會根據(jù)屏幕設(shè)備信息和計算展示區(qū)域所需實(shí)際渲染分辨率,當(dāng)原圖分辨率超出計算所需值,則先調(diào)用壓縮服務(wù)進(jìn)行圖片壓縮,生成渲染所需分辨率的縮略圖,并在聊天區(qū)域進(jìn)行渲染上屏。在這個策略的優(yōu)化下,一般聊天圖片場景測試下來,使用縮略圖比原圖約有 30M ~ 50M 的內(nèi)存優(yōu)化。

    ▲ QQ 優(yōu)化圖片上屏策略

    8、定向優(yōu)化2:可視區(qū)域按需渲染

    8.1DOM 元素數(shù)量

    在 DOM 元素使用數(shù)量我們也有嚴(yán)格的控制,總體采用”所見即占用“的 DOM 渲染策略。在 QQ 大面板中只有視口所見的內(nèi)容才會渲染對應(yīng) DOM 元素。其他所有組件在不渲染展示時,均會移除組件及其 DOM 元素來避免其內(nèi)存開銷。

    ▲ 大虛擬列表控制 DOM 數(shù)量

    尤其對于各個大列表模塊:比如聯(lián)系人列表和群成員列表,DOM 元素都非常多。最開始的內(nèi)測版本中,使用有大量好友和群聊的 QQ 號,窗口平均 DOM 數(shù)達(dá)到 13000。我們將 QQ 所有的普通分頁列表替換為虛擬滾動列表,并且對列表滾動 buffer 進(jìn)行極限壓縮甚至是 0 buffer 。由于不再一味采取空間換時間,沒有 buffer 的情況下必然面對列表滑動性能挑戰(zhàn),因此也需不斷優(yōu)化各類 item 組件渲染性能。

    此外:我們還通過精簡組件 DOM 層級,移除非核心組件 keep-alive(重新優(yōu)化渲染性能)等方式,大賬號使用下整體的 DOM 數(shù)量從 13000 減少到控制在平均 4000 以內(nèi),這部分優(yōu)化減少約 20M 內(nèi)存。

    8.2渲染圖層

    渲染圖層方面,在渲染時滿足某些特殊條件的渲染層,會被瀏覽器自動提升為合成層,達(dá)到提升渲染性能的目的。但是每個合成層都占用額外的內(nèi)存,應(yīng)當(dāng)去掉過量且不必要的合成層來控制圖層帶來的內(nèi)存占用。當(dāng)然結(jié)合渲染性能考量,對于高頻且列表等核心模塊,是可以單獨(dú)提升合成層。

    ▲ QQ 對于渲染合成層的優(yōu)化處理

    在桌面端 QQ 中通過超級調(diào)色盤可以為進(jìn)行色彩換膚,在這個場景中全局各模塊有不少單獨(dú)提升的合成層來實(shí)現(xiàn)毛玻璃、漸變和紋理效果。另外還有許多不經(jīng)意間被提升的隱式合成層。通過對不必要的合成層進(jìn)行移除與合并,整體也優(yōu)化了約 9.3M 內(nèi)存。

    8.3結(jié)構(gòu)化消息

    QQ 支持豐富的消息類型,從簡單的文本、圖文消息,到復(fù)雜的 lottie 表情、下圖所示的業(yè)務(wù)可定制的結(jié)構(gòu)化消息等。我們知道 JavaScript 是單線程的,這些消息同時上屏的時候可能會出現(xiàn)過長的上屏任務(wù)而導(dǎo)致 UI 卡頓,給到用戶的感受就是切換消息列表卡頓,消息上屏慢等糟糕的體驗(yàn)。

    新版 QQ 針對這類復(fù)雜消息上屏,使用了 JavaScript 事件機(jī)制結(jié)合 WebWorker 來實(shí)現(xiàn)消息異步上屏,并使用 OffscreenCanvas +  Worker 池繪制來提升渲染性能。

    ▲ QQ 結(jié)構(gòu)化消息的處理方案

    為了在 Canvas 中實(shí)現(xiàn) CSS 的 Flex 布局效果,我們采用了跨平臺的布局解決方案,將 Yoga 編譯成 WebAssembly 運(yùn)行在 WebWorker 中。Yoga 官方編譯采用的是 asm.js 的方案, 這種方案不支持動態(tài)分配內(nèi)存,可以看到它默認(rèn)分配了一個較高的內(nèi)存,達(dá)到了 128M。

    ▲ Yoga 渲染引擎的原始內(nèi)存占用

    為了優(yōu)化 WebAssembly 的內(nèi)存占用,我們調(diào)整了編譯方式,將 Yoga 編譯成獨(dú)立的 wasm 文件,這種方式相比 asm.js 支持動態(tài)內(nèi)存分配。

    同時結(jié)合聊天窗口的消息卸載策略,經(jīng)過不斷的測試調(diào)優(yōu),在既要保證初始內(nèi)存較少又要盡可能避免內(nèi)存爆發(fā)式增長帶來的性能損耗的前提下,我們把 WebAssembly 的初始內(nèi)存分配優(yōu)化到 2M,再加上對象共享、享元模式等策略,WebWorker 的內(nèi)存占用有了非常可觀的優(yōu)化。

    ▲ QQ 結(jié)構(gòu)化消息渲染引擎優(yōu)化前后的內(nèi)存占用對比

    復(fù)雜的聊天消息雖然是必不可少的功能,但是實(shí)際的消息量還是遠(yuǎn)少于普通的圖文消息,因此在保證用戶體驗(yàn)的前提下,在合適的條件下適時銷毀 WebWorker 是一個合理的策略,而隨著 WebWorker 被銷毀這個線程所占用的內(nèi)存也能被完全釋放。

    9、定向優(yōu)化3:性能與體驗(yàn)的平衡

    9.1Lottie 及動畫方案選型

    超級表情采用 Lottie 動畫技術(shù)方案,有高清高幀率高質(zhì)量特點(diǎn),但同時也為我們帶來了渲染的高成本。

    為了保證 Lottie 的高幀率和減少 CPU 占用,我們緩存了 Lottie 渲染器生成的動畫幀,內(nèi)存消耗成為了首要問題。

    ▲ QQ Lottie 動畫示例

    對其進(jìn)行定量分析:超級表情 Lottie 資源繼承自手機(jī) QQ,尺寸是 512 × 512,動畫幀以 int8 數(shù)組存儲,所以一幀動畫為 512 × 512 × 4 / 1024 bit= 1024 Kb = 1Mb。一個普通大小的超級表情,例如慶祝表情,有 160 幀動畫,依據(jù)緩存 9/10 幀動畫的策略,慶祝表情會占用 144Mb 內(nèi)存。雖然是可回收的,但也無疑是巨大的內(nèi)存消耗。

    關(guān)注到 Lottie 渲染的內(nèi)存消耗后,我們主要從以下 2 步入手:

    1)緩存的動畫幀尺寸:桌面端 lottie 渲染大小為 120 × 120,考慮到需要保持 Lottie 動畫的高質(zhì)量,緩存的動畫幀尺寸調(diào)整為實(shí)際尺寸大小的兩倍,即 240 × 240,降低內(nèi)存消耗 72%。經(jīng)設(shè)計確認(rèn),清淅度上也沒有明顯的差異;

    2)緩存策略:緩存 9/10 的動畫幀減少到緩存 3/4,降低內(nèi)存消耗 35%,而且調(diào)整之后幀率還能得到保障。

    通過以上 2 步,一共降低內(nèi)存消耗 81.8%,慶祝表情從 144 Mb 降低到 35 Mb。

    最后:舊策略對于渲染過且暫時不用的 Lottie 表情,會 buffer 它的第一幀,總共 31 個 Lottie 表情(2.3k * 31 = 7M(最多)),經(jīng)評估之后,我們暫時也拿掉了該策略。

    ▲ QQ Lottie 動畫緩存首幀對內(nèi)存的影響

    另外:桌面 QQ 左側(cè)導(dǎo)航欄目,為了與移動端統(tǒng)一體驗(yàn),使用 Lottie 動畫來實(shí)現(xiàn),從 memory 面板來看, 4 個 icon 導(dǎo)航條會占用約 6M 的內(nèi)存。改用 CSS 實(shí)現(xiàn),不僅效果與 Lottie 的幾乎一致,而且這 6M 的內(nèi)存占用就完全省掉了。

    ▲ QQ 導(dǎo)航條動畫對內(nèi)存的影響

    9.2APNG 動畫優(yōu)化

    APNG 是一個基于 PNG 的位圖動畫格式,后綴名也是.png,在一些類似背景圖的場景下會使用。

    在早期的超級調(diào)色盤中,為了實(shí)現(xiàn)最佳炫彩效果,選用了由 300 張 15KB 靜態(tài)圖合成的配色漸變的 apng 圖片,其大小達(dá)到了 4.2M。

    不過帶來的問題是渲染的延遲感。經(jīng)過和設(shè)計討論,在不影響效果體驗(yàn)的基礎(chǔ)上,進(jìn)行了大量的壓縮,壓縮到了 157KB(壓縮率超過 96%)。

    9.3聊天列表與消息

    聊天列表 AIO,作為 QQ IM 模塊中最主要的承載消息數(shù)據(jù)展示模塊,其滾動體驗(yàn)必然離不開用戶體驗(yàn)與內(nèi)存的權(quán)衡。

    聊天列表在靜態(tài)與滾動過程中,維持消息組件的數(shù)量多少決很大程度決定整個 QQ 的內(nèi)存占用。消息數(shù)據(jù)從服務(wù)端拉取后會存儲在本地 DB,根據(jù)策略會將當(dāng)前會話的消息數(shù)據(jù)緩存在內(nèi)存中。

    隨著滾動加載,消息緩存占用的內(nèi)存也越多。所以也有一定動態(tài)閾值的策略,丟棄滾動方向相反的舊消息,從而將內(nèi)存控制在可接受范圍。如果用戶重新操作又需要加載時,這請求底層向本地磁盤 DB 重新拉取。

    ▲ QQ 聊天消息列表的加載策略

    消息組件實(shí)例是內(nèi)存占用的大戶,每條消息組件內(nèi)部包含頭像 / 昵稱 / 狀態(tài) / 內(nèi)容等多個實(shí)例,如果不對消息實(shí)例進(jìn)行回收銷毀,每百條消息約能帶來 20M+ 的內(nèi)存增量,因此消息實(shí)例的回收策略尤為關(guān)鍵。

    最早版本中對消息上屏沒有丟棄策略,內(nèi)存增量沒有很好控制。于是采用分頁列表,屏內(nèi)保持固定幾頁消息(約 30 ~ 50 條消息,視屏幕尺寸決定),超過范圍的消息進(jìn)行丟棄,列表高度由屏內(nèi)消息直接撐起,用戶通過觸頂或觸底進(jìn)行上下一頁消息的加載。

    但這頁帶來些點(diǎn)問題:一方面隨著觸頂觸底,滾動條頻繁跳動的體驗(yàn)并不好。另一方面列表高度由不定高的組件渲染消息來維持,不得不始終保留 30 ~ 50 條消息以撐起滾動高度,不可見消息的那部分便造成內(nèi)存的浪費(fèi)。

    使用虛擬列表維持計算高度后,列表不再依賴保持真實(shí)消息內(nèi)容的渲染,理論上我們可以將可視區(qū)域以外的消息實(shí)例全部銷毀,僅保留用戶可見的消息,最大程度地壓縮消息實(shí)例數(shù)量,指保留很少的 buffer 消息實(shí)例。在實(shí)際滾動中由于消息實(shí)例在滾動過程被不斷創(chuàng)建和銷毀,占用主線程,影響 UI 繪制和用戶輸入。

    因此我們還做了:

    • 1)對創(chuàng)建銷毀做一定聚合,批量處理消息上屏;
    • 2)精簡優(yōu)化單條組件的渲染性能;
    • 3)不同滾動方向調(diào)整上下不同 buffer 大小 等等措施;
    • 4)會話切換和窗口聚失焦最小化等操作時對不再使用的消息資源內(nèi)存進(jìn)行主動回收。

    ▲ QQ 聊天消息列表的上屏策略

    滾動性能和內(nèi)存占用之間需要取得平衡,既要最大程度壓縮上屏消息數(shù)量以節(jié)省內(nèi)存,又要保證滾動性能體驗(yàn)。然而經(jīng)過優(yōu)化后,本地測試加載 200 條混合種類的消息場景下,從空狀態(tài)進(jìn)入聊天會話中,消息列表內(nèi)存增量從最多 44.2M 降至 6.1M,且滾動靜止后內(nèi)存不會任意增長。

    10、定向優(yōu)化4:Electron的正確使用姿勢

    Electron 給主進(jìn)程提供了不少對系統(tǒng)能力調(diào)用的 API,如托盤、系統(tǒng)通知、macOS 中 dock 欄設(shè)置等。但是如果對這些 Electron 能力的使用方式不對,就可能導(dǎo)致不必要的大量內(nèi)存占用甚至是泄漏。

    比如 QQ 中:我們通過短間隔定時調(diào)用 Tray setImage API 來實(shí)現(xiàn) QQ 托盤的閃爍,如果不注意傳入 string Path 則會每次創(chuàng)建 Image 對象導(dǎo)致內(nèi)存占用,正確的方式應(yīng)該創(chuàng)建 NativeImage 并緩存,調(diào)用 Tray setImage 傳入指定 NativeImage,避免反復(fù)創(chuàng)建 Image 導(dǎo)致的內(nèi)存問題。

     

    ▲ Windows 托盤圖標(biāo)內(nèi)存泄漏定位

    類似的問題還有在 Mac OS 中調(diào)用 API dock.setIcon 也會持續(xù)占用約 20M 的 CGImage 位圖內(nèi)存,正確的方案應(yīng)該是不通過 Electron API 指定,而是通過打包 plist(屬性文件) 指定 dock 欄圖標(biāo)。

    ▲ Mac OS dock 圖標(biāo)內(nèi)存泄漏定位

    在使用 Electron 的過程中,還存在類似會導(dǎo)致內(nèi)存問題的使用方式,我們需要結(jié)合客戶端內(nèi)存工具進(jìn)行深度挖掘和分析,才能發(fā)現(xiàn)和處理這些問題。

    11、定向優(yōu)化5:消滅內(nèi)存泄漏

    我們知道 V8 有自己的垃圾回收機(jī)制,雖然它在 GC(垃圾回收)方面有著其各種策略,并做了各種優(yōu)化從而盡可能的確保垃圾得以回收,但我們?nèi)詰?yīng)當(dāng)避免任何可能導(dǎo)致無法回收的代碼操作。

    常見的例子包括:

    • 1)未移除的監(jiān)聽器和定時器:在監(jiān)聽事件處理函數(shù)其中引用的不被釋放導(dǎo)致的泄漏;
    • 2)游離 DOM 未釋放:移出 document 后游離 DOM 仍存在引用導(dǎo)致無法釋放。較多發(fā)生于框架的組件銷毀時,相關(guān)監(jiān)聽未取消導(dǎo)致組件沒有釋放的情況;
    • 3)監(jiān)控 / 打點(diǎn)導(dǎo)致的泄漏:在使用 Performance.mark 打點(diǎn)監(jiān)控時,產(chǎn)生 PerformanceMark 對象,在用完之后沒有手動清除,也會導(dǎo)致內(nèi)存泄漏;
    • 4)console.error 導(dǎo)致的泄漏:控制臺持有被打印對象始終不釋放,導(dǎo)致應(yīng)用的泄漏;
    • 5)其他不當(dāng)?shù)拈]包及隱式的全局變量。

    以上是桌面 QQ 在早期遇到的常見問題。后續(xù),我們通過代碼檢測手段來防范這類問題的出現(xiàn)。

    與一般的前端項目不同,由于桌面 QQ 的長周期使用特性,任何緩慢而微小的內(nèi)存泄漏都可能被放大,這也是我們極力把控并阻止任何可能導(dǎo)致內(nèi)存泄漏的代碼引入的原因。

    12、優(yōu)化結(jié)果與線上監(jiān)控

    經(jīng)過一系列組合優(yōu)化之后,在我們自己的設(shè)備上來看,QQ 的內(nèi)存使用基本是達(dá)標(biāo)了,長時間掛機(jī)穩(wěn)定在 300M 以下,但在廣大 QQ 用戶側(cè)能否保持這個水平?只有通過線上內(nèi)存及性能的采集監(jiān)控,才有數(shù)據(jù)指標(biāo)來觀測,從而才能對優(yōu)化有效性進(jìn)行驗(yàn)證和決定如何調(diào)整優(yōu)化方向。

    好在 Electron 提供了 app.getMetics 、 process.getMemoryInfo 等 API 來采集內(nèi)存指標(biāo)。

    但需要注意的是:這些 API 所采集返回的內(nèi)存值的真實(shí)含義,如 getMetric 所采集的到 workingsetSize 和 privateBytes 均不是任務(wù)管理器用戶所看到的內(nèi)存。

    這里我們通過 patch 定制改造 Electron getMetics API,來增加不同平臺任務(wù)管理器的內(nèi)存類型的指標(biāo),并且采集包含了主進(jìn)程、渲染進(jìn)程、GPU 進(jìn)程和工具進(jìn)程等所有內(nèi)存指標(biāo)。

    為了避免頻繁采集上報內(nèi)存指標(biāo)所帶來的的性能消耗,我們設(shè)定了一定時間的采集間隔,同時針對使用場景的采集做了抽樣。并將渲染進(jìn)程 pid 映射尋找窗口名,只在若干次采集后再做聚合計算,通過 SDK 上報到 prometheus + grafana 的指標(biāo)觀測平臺。

    ▲ QQ 內(nèi)存監(jiān)控整體方案

    經(jīng)過若干次內(nèi)存性能優(yōu)化的迭代,目前從線上數(shù)據(jù)指標(biāo)來看,新版 Windows QQ 運(yùn)行的內(nèi)存在主場景下基本控制在 300M,這個值已經(jīng)基本達(dá)到我們設(shè)定的目標(biāo)。

    從登錄后使用過程中的內(nèi)存指標(biāo)如下:

    • 1)整體應(yīng)用的內(nèi)存平均占用約為 228M;
    • 2)中位數(shù)占用約為 211M;
    • 3)90% 分位用戶內(nèi)存占用約為 350M。

    當(dāng)然,這個目標(biāo)只是階段性的,我們還會持續(xù)針對更多使用場景進(jìn)行內(nèi)存優(yōu)化。

    13、防劣化與自動化測試

    為了持續(xù)關(guān)注和保證新版 QQ 項目的性能達(dá)標(biāo)且不劣化,除了比較常規(guī)的單元測試、代碼檢查、代碼評審機(jī)制、框架內(nèi)置一些開發(fā)規(guī)范等手段外,我們還在建設(shè)一個防劣化平臺,主要通過自動化的端對端 (e2e) 測試來持續(xù)監(jiān)控項目集成后的性能變化。

    主要是:

    • 1)定時對主干上集成構(gòu)建的程序進(jìn)自動化 e2e 測試;
    • 2)除了對功能的冒煙測試外,針對重點(diǎn)關(guān)注的性能指標(biāo),構(gòu)造了對應(yīng)的帳號和環(huán)境,編輯特定的用例,用于采集性能指標(biāo);
    • 3)通過將采集和采樣的指標(biāo)上報到防劣化的監(jiān)控平臺,來監(jiān)控項目集成后的性能變化,如會話切換響應(yīng)時間、內(nèi)存占用、CPU 使用率等;
    • 4)監(jiān)控平臺提供按版本和時間的指標(biāo)曲線、對比,方便查看和分析性能變化情況。同時打通企業(yè)微信機(jī)器人,對性能指標(biāo)情況進(jìn)行實(shí)時推送告警。

    根據(jù)告警信息對應(yīng)的版本信息和代碼記錄,排查情況,閉環(huán)問題。

    ▲ 防劣化機(jī)制示意圖

    這一套機(jī)制之前在 內(nèi)測中的 QQ 頻道桌面端的項目中嘗試應(yīng)用,運(yùn)行發(fā)現(xiàn)了一些比較典型的代碼異常、crash、oom 問題,證明確實(shí)有效。新版 QQ 業(yè)務(wù)和設(shè)計都更復(fù)雜,建設(shè)好防劣化機(jī)制無論是對發(fā)現(xiàn)問題的效率,還是對整體的性能和質(zhì)量都是意義重大的,也是我們團(tuán)隊當(dāng)前重點(diǎn)建設(shè)、未來持續(xù)迭代的重要任務(wù)。

     

    ▲ 防劣化推送與告警實(shí)際應(yīng)用圖例

    14、本文小結(jié)

    可能大家比較關(guān)心,為什么一定要選擇 Electron?

    其實(shí)我們是經(jīng)過深思熟慮的:

    首先:全新 QQ 意味著我們應(yīng)該專注在功能快速迭代上,否則,以 QQ 的體量戰(zhàn)線會拉得非常長。

    我們希望最后選擇的跨平臺方案應(yīng)該是足夠成熟、低開發(fā)和使用成本,不需要為了使用框架本身,還需要投入額外巨大的人力成本。這個其實(shí)在 React Native、Flutter、Tauri 等跨平臺框架的使用過程中,我們都遇到過類似的問題,除了功能開發(fā),為了把框架生態(tài)、周邊、工具鏈建設(shè)好,還需要投入巨大的額外成本,Qt 也有類似的問題。

    而使用 Electron,對于 Web 前端開發(fā)同學(xué),基本上是 0 成本,現(xiàn)有的 Web 前端的大部分基建都可以直接復(fù)用,而且使用 Web 開發(fā) UI 的效率,在主流技術(shù)棧里算是很高的了。

    并且這幾年主流的桌面端應(yīng)用基本都選擇了 Electron,如 VScode、Discord、Slack、Skype、Whatsapp、Figma 等等,新的桌面應(yīng)用基本上也是首選 Electron。

    另外,Electron 版本的迭代速度和社區(qū)氛圍都很在線。

    其次:從結(jié)果或者解決問題的角度來看,經(jīng)過一系列優(yōu)化之后基本可以將 QQ 核心聊天場景的內(nèi)存控制在 300M 以內(nèi),150M 的安裝包大小,與舊版純 Native QQ 差別較小。不單單內(nèi)存占用,其他核心體驗(yàn),比如切 AIO 的流暢度上要優(yōu)于舊版 QQ。即便是在今天,QQ 也堅定一年半之前選擇了 Electron。

    最后:讓我們再次聚焦在內(nèi)存優(yōu)化的工作上,下圖是我們在桌面 QQ 中針對 Electron 內(nèi)存優(yōu)化工作的一個概覽。

    ▲ 桌面 QQ 內(nèi)存優(yōu)化工作概覽

    內(nèi)存優(yōu)化沒有銀彈,有的只是一步一個腳印深入做下去,芝麻西瓜都要撿,從量變到質(zhì)變。

    未來我們完全有信心,憑著已有的經(jīng)驗(yàn)和對其技術(shù)的理解,守住現(xiàn)在這些成果的同時,進(jìn)一步優(yōu)化 QQ 生態(tài)下的各個子業(yè)務(wù)、子模塊的內(nèi)存占用問題。

    因此,也希望通過我們實(shí)踐經(jīng)驗(yàn)分享, 讓大家從更多辯證的視角來重新看待 Electron 或類 CEF 的技術(shù)方案。

    15、相關(guān)資料

    [1] Electron官方開發(fā)者手冊

    [2] 快速了解新一代跨平臺桌面技術(shù)——Electron

    [3] Electron初體驗(yàn)(快速開始、跨進(jìn)程通信、打包、踩坑等)

    [4] Electron 基礎(chǔ)入門 簡單明了,看完啥都懂了

    [5] vivo的Electron技術(shù)棧選型、全方位實(shí)踐總結(jié)

    [6] 融云基于Electron的IM跨平臺SDK改造實(shí)踐總結(jié)

    [7] 閑魚IM基于Flutter的移動端跨端改造實(shí)踐

    [8] 網(wǎng)易云信基于Electron的IM消息全文檢索技術(shù)實(shí)踐

    [9] 閑話即時通訊:騰訊的成長史本質(zhì)就是一部QQ成長史

    [10] 技術(shù)往事:創(chuàng)業(yè)初期的騰訊——16年前的冬天,誰動了馬化騰的代碼

    [11] 技術(shù)往事:史上最全QQ圖標(biāo)變遷過程,追尋IM巨人的演進(jìn)歷史

    [12] QQ的成功,遠(yuǎn)沒有你想象的那么順利和輕松

    [13] 還原真實(shí)的騰訊:從最不被看好,到即時通訊巨頭的草根創(chuàng)業(yè)史

    附錄:更多QQ團(tuán)隊分享的技術(shù)文章

    騰訊技術(shù)分享:騰訊是如何大幅降低帶寬和網(wǎng)絡(luò)流量的(圖片壓縮篇)

    騰訊技術(shù)分享:騰訊是如何大幅降低帶寬和網(wǎng)絡(luò)流量的(音視頻技術(shù)篇)

    騰訊技術(shù)分享:Android版手機(jī)QQ的緩存監(jiān)控與優(yōu)化實(shí)踐

    騰訊技術(shù)分享:Android手Q的線程死鎖監(jiān)控系統(tǒng)技術(shù)實(shí)踐

    讓互聯(lián)網(wǎng)更快:新一代QUIC協(xié)議在騰訊的技術(shù)實(shí)踐分享

    騰訊技術(shù)分享:社交網(wǎng)絡(luò)圖片的帶寬壓縮技術(shù)演進(jìn)之路

    QQ音樂團(tuán)隊分享:Android中的圖片壓縮技術(shù)詳解(上篇)

    QQ音樂團(tuán)隊分享:Android中的圖片壓縮技術(shù)詳解(下篇)

    騰訊團(tuán)隊分享:手機(jī)QQ中的人臉識別酷炫動畫效果實(shí)現(xiàn)詳解

    騰訊團(tuán)隊分享 :一次手Q聊天界面中圖片顯示bug的追蹤過程分享

    QQ 18年:解密8億月活的QQ后臺服務(wù)接口隔離技術(shù)

    以手機(jī)QQ為例探討移動端IM中的“輕應(yīng)用”

    騰訊原創(chuàng)分享(一):如何大幅提升移動網(wǎng)絡(luò)下手機(jī)QQ的圖片傳輸速度和成功率

    騰訊原創(chuàng)分享(二):如何大幅壓縮移動網(wǎng)絡(luò)下APP的流量消耗(下篇)

    騰訊原創(chuàng)分享(三):如何大幅壓縮移動網(wǎng)絡(luò)下APP的流量消耗(上篇)

    信鴿團(tuán)隊原創(chuàng):一起走過 iOS10 上消息推送(APNS)的坑

    騰訊信鴿技術(shù)分享:百億級實(shí)時消息推送的實(shí)戰(zhàn)經(jīng)驗(yàn)

    IPv6技術(shù)詳解:基本概念、應(yīng)用現(xiàn)狀、技術(shù)實(shí)踐(上篇)

    IPv6技術(shù)詳解:基本概念、應(yīng)用現(xiàn)狀、技術(shù)實(shí)踐(下篇)

    騰訊TEG團(tuán)隊原創(chuàng):基于MySQL的分布式數(shù)據(jù)庫TDSQL十年鍛造經(jīng)驗(yàn)分享

    了解iOS消息推送一文就夠:史上最全iOS Push技術(shù)詳解

    騰訊資深架構(gòu)師干貨總結(jié):一文讀懂大型分布式系統(tǒng)設(shè)計的方方面面

    騰訊音視頻實(shí)驗(yàn)室:使用AI黑科技實(shí)現(xiàn)超低碼率的高清實(shí)時視頻聊天

    騰訊技術(shù)分享:微信小程序音視頻與WebRTC互通的技術(shù)思路和實(shí)踐

    騰訊技術(shù)分享:GIF動圖技術(shù)詳解及手機(jī)QQ動態(tài)表情壓縮技術(shù)實(shí)踐

    社交軟件紅包技術(shù)解密(一):全面解密QQ紅包技術(shù)方案——架構(gòu)、技術(shù)實(shí)現(xiàn)等

    社交軟件紅包技術(shù)解密(九):談?wù)勈諵紅包的功能邏輯、容災(zāi)、運(yùn)維、架構(gòu)等

    社交軟件紅包技術(shù)解密(十):手Q客戶端針對2020年春節(jié)紅包的技術(shù)實(shí)踐

    QQ設(shè)計團(tuán)隊分享:新版 QQ 8.0 語音消息改版背后的功能設(shè)計思路

    微信技術(shù)分享:揭秘微信后臺安全特征數(shù)據(jù)倉庫的架構(gòu)設(shè)計

    IM跨平臺技術(shù)學(xué)習(xí)(九):全面解密新QQ桌面版的Electron內(nèi)存占用優(yōu)化


    (本文已同步發(fā)布于:http://www.52im.net/thread-4429-1-1.html



    作者:Jack Jiang (點(diǎn)擊作者姓名進(jìn)入Github)
    出處:http://www.52im.net/space-uid-1.html
    交流:歡迎加入即時通訊開發(fā)交流群 215891622
    討論:http://www.52im.net/
    Jack Jiang同時是【原創(chuàng)Java Swing外觀工程BeautyEye】【輕量級移動端即時通訊框架MobileIMSDK】的作者,可前往下載交流。
    本博文 歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明出處(也可前往 我的52im.net 找到我)。


    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導(dǎo)航:
     
    Jack Jiang的 Mail: jb2011@163.com, 聯(lián)系QQ: 413980957, 微信: hellojackjiang
    主站蜘蛛池模板: 成人片黄网站色大片免费观看APP| 猫咪免费人成在线网站| 免费在线视频一区| 免费三级毛片电影片| 免费鲁丝片一级观看| 亚洲国产成人精品久久久国产成人一区二区三区综 | 亚洲伊人久久大香线蕉AV| 国产精品亚洲色图| 久久免费精品一区二区| 91精品成人免费国产| 狼群影院在线观看免费观看直播| 巨胸喷奶水视频www网免费| 亚洲成人在线免费观看| 国产高清免费的视频| 国产精品国产免费无码专区不卡| 亚洲日韩人妻第一页| 337p日本欧洲亚洲大胆色噜噜| 久久夜色精品国产亚洲AV动态图| 亚洲乱人伦精品图片| 亚洲粉嫩美白在线| 一级视频免费观看| 二个人看的www免费视频| 国产精品免费看久久久香蕉| 精品人妻系列无码人妻免费视频 | 午夜老司机永久免费看片| 好先生在线观看免费播放| 国产亚洲情侣一区二区无码AV| 中文字幕久久亚洲一区| 亚洲深深色噜噜狠狠爱网站| 国产AV无码专区亚洲Av| 亚洲人成色777777老人头| 国产精品亚洲精品日韩电影| 99久久精品国产免费| 四虎免费永久在线播放| 国产成人A亚洲精V品无码| 亚洲一区精彩视频| 瑟瑟网站免费网站入口| 中文字幕免费在线看线人| 相泽亚洲一区中文字幕| 亚洲欧洲无卡二区视頻| 无码免费一区二区三区免费播放 |