国产精品久久久aaaa,日日干夜夜操天天插,亚洲乱熟女香蕉一区二区三区少妇,99精品国产高清一区二区三区,国产成人精品一区二区色戒,久久久国产精品成人免费,亚洲精品毛片久久久久,99久久婷婷国产综合精品电影,国产一区二区三区任你鲁

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

WebGL/Canvas 內(nèi)存泄露分析

圖撲-數(shù)字孿生 ? 來源:圖撲-數(shù)字孿生 ? 作者:圖撲-數(shù)字孿生 ? 2025-10-21 11:40 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

在構(gòu)建高性能、長周期運行的 WebGL/Canvas 應(yīng)用(如 3D 編輯器、數(shù)據(jù)可視化平臺)時,內(nèi)存管理是一個至關(guān)重要且極具挑戰(zhàn)性的課題。

wKgZO2j3AJyAE7hqAAI_IQeOhV0053.png

開發(fā)者通常面臨的內(nèi)存泄漏問題,其根源遠比簡單的 JavaScript 對象未釋放要復(fù)雜得多。一個現(xiàn)代 WebGL/Canvas 應(yīng)用的內(nèi)存版圖實際上跨越了三個截然不同但又相互關(guān)聯(lián)的內(nèi)存區(qū)域:

圖 V8 引擎管理的 JavaScript 堆(JS Heap),絕大部分情況最關(guān)注的是這一層的泄露

圖形處理器GPU)的顯存(VRAM)

Blink 渲染引擎自身用于管理 DOM 等對象的原生 C++ 堆(Native Heap)

這三個內(nèi)存區(qū)域各自遵循不同的分配、管理和回收規(guī)則:

V8 堆:采用先進的、自動化的垃圾回收(GC)機制,當引用為空的時候會自動釋放

GPU 顯存:依賴于開發(fā)者通過 WebGL API 進行顯式的手動管理

Blink 的原生堆:由專用 C++ 垃圾回收器負責

大多數(shù)難以診斷和修復(fù)的內(nèi)存泄漏問題,其本質(zhì)都源于對這三個層面之間的邊界、所有權(quán)規(guī)則以及通信協(xié)議缺乏深刻理解。

我們將分別對三個核心部分,系統(tǒng)性地分析每一層內(nèi)存區(qū)域中常見的泄漏模式、底層成因,并介紹實用排查策略和解決方案。通過本篇分享,開發(fā)者將能夠建立一個貫穿 GPU、JavaScript 引擎和瀏覽器渲染內(nèi)核的整體內(nèi)存心智模型,從而更有效地構(gòu)建穩(wěn)定、高效且無泄漏的 WebGL/Canvas 應(yīng)用。

wKgZPGj3AJ2AXfqJAAEkL65J2mY145.png

JavaScript 堆泄漏

堆簡述

Javascript 的解釋器 V8 引擎將瀏覽器內(nèi)存分為兩個主要部分:

■棧(Stack):用于存儲靜態(tài)數(shù)據(jù),包括原始類型(Primitive Types)的局部變量(如 number, boolean, null, undefined, string 等)以及指向堆中對象的指針(引用地址)。棧內(nèi)存的特點是大小固定、自動分配和釋放,隨著函數(shù)調(diào)用的開始和結(jié)束(執(zhí)行上下文的入棧和出棧)而進行管理。

■堆(Heap):用于存儲動態(tài)分配的內(nèi)存,即大小不固定的、生命周期可能很長的數(shù)據(jù)。JavaScript 中的絕大多數(shù)“東西”都存在這里。譬如對象(object),數(shù)組(Arrays),函數(shù)(Functions),閉包(Clousures),字符串(String),ArrayBuffer / Uint8Array 等。

我們重點關(guān)注堆上的資源,盡管有自動垃圾回收(GC)機制,JS 堆泄漏仍然是 WebGL 應(yīng)用中一個常見且棘手的問題。

導(dǎo)致 JS 內(nèi)存泄漏的常見架構(gòu)模式:

在 JavaScript 這類具備自動垃圾回收機制的語言中,內(nèi)存泄漏的本質(zhì)并非“忘記釋放內(nèi)存”,而是存在“意外的引用”(unwanted reference)。一個在邏輯上已經(jīng)廢棄、應(yīng)用不再需要的對象,若仍有一條引用鏈將其與存活的對象圖相連,GC 就會判定它“可達”,從而無法回收 。

GC 標記 - 清除(Mark-and-Sweep)算法遵循明確的規(guī)則:從根對象開始,逐條追蹤所有指針。只要從根到某個對象存在一條路徑,該對象就定義為可達,即“存活”。但是 GC 無法理解開發(fā)者的語義意圖 —— 它無法判斷一個已經(jīng)脫離文檔的 DOM 節(jié)點是否永遠不會被重新掛載,也無法知曉閉包中捕獲的變量是否永遠不會被訪問。它只能機械性地遵循指針。

因此,修復(fù)這些內(nèi)存泄漏并非尋找 V8 引擎的 bug,而是細致地管理對象圖,確保當對象在邏輯上不再需要時,通過 myNode = null 或 removeEventListener 等方式顯式地切斷引用。

以下是一些常見的導(dǎo)致意外引用的模式:

1、 分離的 DOM 元素

分離的 DOM 元素 (Detached DOM Elements) 這是最經(jīng)典的泄漏模式之一。當一個 DOM 節(jié)點通過 element.removeChild() 從文檔樹中移除后,它在頁面上就不再可見。但是,如果此時 JavaScript 代碼中仍有某個變量持有對該節(jié)點的引用,那么這個節(jié)點及其整個子樹都無法被 GC 回收。在復(fù)雜的單頁應(yīng)用(SPA)中,視圖組件被動態(tài)創(chuàng)建和銷毀,如果銷毀邏輯不完善,很容易留下對舊視圖 DOM 節(jié)點的引用。

2、閉包引起的意外作用域捕獲

閉包(Closure)是 JavaScript 的一個強大特性,它允許函數(shù)訪問并操作其詞法作用域(lexical scope)中的變量,即使該函數(shù)已在其作用域之外被調(diào)用。但是這份強大也暗藏風(fēng)險,閉包往往是內(nèi)存泄漏的“隱形源頭”。

閉包會完整持有其創(chuàng)建時所在作用域的引用。若一個生命周期很長的內(nèi)部函數(shù)(例如,一個事件回調(diào)或定時器回調(diào))是在一個包含大型對象引用的外部函數(shù)中創(chuàng)建的,那么這個大型對象也會被閉包“捕獲”。即使內(nèi)部函數(shù)本身從未使用過它,大型對象也會始終處于可達狀態(tài),最終導(dǎo)致垃圾回收器無法對其回收,從而造成內(nèi)存泄漏。

3、 懸空的定時器和事件監(jiān)聽器

傳遞給 setInterval、setTimeout 或 element.addEventListener 的回調(diào)函數(shù),其生命周期會持續(xù)到定時器被清除或事件監(jiān)聽器被移除為止。在此期間,若回調(diào)函數(shù)內(nèi)部引用了其他對象(比如某個組件的實例或數(shù)據(jù)),這些被引用的對象也會被“綁定”而保持存活狀態(tài)。在組件化開發(fā)中,最常見的疏漏之一便是:在組件銷毀時忘記清理這些定時器與事件監(jiān)聽器。這就會直接導(dǎo)致整個組件實例及其依賴對象始終處于可達狀態(tài),最終無法被回收,從而造成內(nèi)存泄漏。

4、意外的全局變量

在非嚴格模式下,函數(shù)內(nèi)給未聲明的變量進行賦值,JavaScript 不會報錯,反而會在全局對象(如瀏覽器的 window)上創(chuàng)建一個同名變量。全局變量作為 GC 根節(jié)點,它們在應(yīng)用的整個生命周期內(nèi)都無法被回收。這種“意外的全局變量”通常由拼寫錯誤或忘記使用 let、const、var 關(guān)鍵字引起,是一種隱蔽但危害嚴重的內(nèi)存泄漏源。

Chrome DevTools 堆分析實戰(zhàn)指南

Chrome DevTools(開發(fā)者工具) 的 Memory(內(nèi)存)面板是診斷 JS 堆泄漏的權(quán)威工具。它可以對當前堆進行快照,直觀的展示當前的占用情況。

wKgZO2j3AJ2AeJoCAAChRr8sHuM829.png

具體操作位置在 Chrome DevTools -> Momery 標簽頁(圖中 ①)。

1.在內(nèi)存分析中,Heap snapshot(堆快照)是最常用的排查手段,在生成快照前,需先選擇這一類型(圖中 ②)。

2.在生成快照前,需要先點擊上圖中的 ③ 號按鈕(強制垃圾回收),待完成一次 GC 后,再點 ④ 號按鈕生成快照。這樣做的原因是,我們的核心目標是排查內(nèi)存泄漏問題,強制 GC 能釋放原本應(yīng)該被回收的資源,這會讓快照結(jié)果更加直觀地顯示出問題。

3.快照生成后,在 ⑤ 位置會顯示快照信息,展開后如下:

(⑥ 位置會展示堆內(nèi)存的大小,能快速且直觀地了解到整個頁面的堆內(nèi)存占用情況。)

wKgZPGj3AJ6AAl6PAAFNfc9O8xQ880.png

在快照信息中,需要重點關(guān)注每個對象的兩項核心數(shù)據(jù):

Shallow Size 淺層大小(圖中 ①):一般用來指對象自身占用的大小,不包含它引用的其他對象的大小。

Retained Size 保留大小(圖中 ②):表示該對象在被 GC 后,所能釋放的總內(nèi)存大小。通常等于自身的 Shallow Size 加上被它引用的其他對象的 Shallow Size 之和。

在實際分析中,建議優(yōu)先關(guān)注 Retained Size,因其能更全面地反映對象堆內(nèi)存占用的實際影響。

快照的摘要視圖

wKgZO2j3AJ6AfLAoAADcEM1NGlI567.png

在上圖所示的摘要中,每一項都支持展開,展開后可以看到對象的完整引用鏈。摘要面板適合的運用場景:當單次 Profile 已顯示出大量的內(nèi)存占用時,可先按 Retained Size 對列表進行排序,快速定位到占據(jù)了過高的內(nèi)存的項,展開其中的可疑目標并一路追溯,直到找到根源 —— 通常是掛載到全局 windows 對象上的變量,或被閉包捕獲的變量。

三快照法(推薦的排查步驟)

在多數(shù)情況下,泄露是緩慢發(fā)生的,單個堆快照包含了數(shù)百萬個對象,雜亂無章,不方便直接找到泄漏源。因此,我們更推薦使用“三快照法”來找到泄露的源頭。具體操作步驟:

1.快照 1 (基線狀態(tài)):加載頁面,在應(yīng)用進入穩(wěn)定狀態(tài)后,點快照中的掃把按鈕,做一次強制 GC 后,拍攝第一次堆快照(Heap snapshot),建立內(nèi)存的基線

2.執(zhí)行可疑操作:執(zhí)行一系列你懷疑可能導(dǎo)致內(nèi)存泄漏的用戶操作。這里的關(guān)鍵在于:這個操作序列應(yīng)具備是可逆性。例如“打開一個復(fù)雜的 UI 面板,隨后再將其關(guān)閉”。這個“操作-逆操作”循環(huán)是你的受控實驗,假設(shè)是“該循環(huán)應(yīng)是內(nèi)存中性的,即操作后不應(yīng)遺留任何內(nèi)存垃圾”。此外,也可測試應(yīng)用長時間靜置(如半小時以上)的情況。

3.快照 2:做完上述的操作之后,繼續(xù)強制 GC 一次,再拍攝第二次快照。

4.放大泄漏:重復(fù)執(zhí)行步驟 2 中的“操作-逆操作”循環(huán)數(shù)次(例如 1-N 次)。這會放大內(nèi)存泄漏,使其在快照對比中更加明顯。

5.快照 3:完成所有循環(huán)后,再次強制 GC,并拍攝第三次快照。

1、使用對比視圖

在完成以上的操作步驟后,選擇第三個快照,并在頂部的視圖選擇器中(下圖 ②),將視圖模式從 Summary 切換為 Comparison,比較對象選擇為快照 2(下圖 ③)。現(xiàn)在視圖只會顯示快照 2 和快照 3 之間發(fā)生變化的對象。操作后需要關(guān)注以下內(nèi)容:

Delta 列:這是該視圖的核心,它顯示了對象實例數(shù)量的凈變化。需重點關(guān)注 Delta 值為正數(shù)的項,尤其是那些與重復(fù)操作次數(shù)成正比的構(gòu)造函數(shù)。這些就是在操作循環(huán)中被創(chuàng)建但未能被成功回收的對象。

Retained Size Delta 列:此列顯示了該類對象及其引用的所有對象所占內(nèi)存的凈增量。按此列降序排序,可以快速定位到對內(nèi)存影響最大的泄漏源。

wKgZPGj3AJ-Ac-atAAD29LY8eG0032.png

2、使用摘要視圖

還有一種很重要的排查方式:

選擇第三個快照,頂部的視圖選擇器,切換為 Summary

右側(cè)下拉框中選擇篩選快照一和快照二中間創(chuàng)建的對象

該視圖的意圖是:查找出快照 2 較快照 1 新增的內(nèi)存對象,若這些新增對象在快照 3 中依然存在,那么它們極有可能是泄露的源頭。

wKgZO2j3AJ-AaYTXAAEVV9gq8vE916.png

3、使用 Retainers 樹追溯泄漏源

在對比視圖中定位到一個可疑的泄漏對象(即對應(yīng)的構(gòu)造函數(shù))后,展開該構(gòu)造函數(shù),并選中其中一個實例。此時,下方的 Retainers(保留者)面板會自動加載內(nèi)容。這個面板是定位內(nèi)存泄漏根源的核心工具,面板展示了一條或多條引用鏈,并清晰地解釋了被選中對象無法被 GC 回收的原因。

具體分析步驟如下:

追溯引用鏈:Retainers 樹以被選中的對象為起點,逐層向上追溯,直到指向某個 GC 根節(jié)點(例如 (Global handles) 下的 window 對象)。開發(fā)者需要仔細檢查這條鏈路上的每個節(jié)點。

識別意外引用:尋找那些本應(yīng)在操作結(jié)束后被切斷的引用。例如,一個已關(guān)閉面板的 DOM 節(jié)點,仍被一個全局緩存對象 myApp.cache 引用,那么 myApp.cache 就是那個“意外的引用”。

關(guān)注高亮節(jié)點:分析分離的 DOM 樹時,DevTools 會用顏色高亮節(jié)點。

黃色節(jié)點: 表示被 JavaScript 代碼直接引用的節(jié)點。

紅色節(jié)點:表示無直接引用,但因?qū)儆谀硞€黃色節(jié)點的父子節(jié)點,而被間接保留在內(nèi)存中的節(jié)點。在排查時,應(yīng)優(yōu)先關(guān)注黃色節(jié)點。

wKgZPGj3AKCAdC8mAAEG80Jw1ak147.png

GPU 顯存與 WebGL 上下文管理

本部分內(nèi)容將聚焦于 GPU 中的關(guān)鍵資源,此類資源必須通過 WebGL API 進行顯式的、手動的生命周期管理。這背后的核心邏輯在于:在 GPU 層面不存在自動內(nèi)存管理機制。從資源的創(chuàng)建、綁定到最終銷毀,開發(fā)者須全程主導(dǎo),主動承擔釋放內(nèi)存的全部責任。

WebGL 上下文句柄

WebGL 上下文句柄是一種有限且關(guān)鍵的資源。現(xiàn)代瀏覽器對單個頁面或同源(origin)下可創(chuàng)建的活動 WebGL 上下文(Context)數(shù)量施加了嚴格的限制。例如,在 Chrome 瀏覽器中,這個上限通常是 16 個。Firefox 也有類似的限制,盡管具體數(shù)值和配置策略可能略有不同。

這個限制是瀏覽器廠商為保護整個系統(tǒng)穩(wěn)定性而采取的一項關(guān)鍵防御措施。GPU 是一種系統(tǒng)級的共享資源,如果單個網(wǎng)頁能夠無限制地創(chuàng)建 WebGL 上下文,它將可能耗盡 GPU 驅(qū)動程序的資源,導(dǎo)致驅(qū)動崩潰或整個操作系統(tǒng)的性能下降,從而影響到其他應(yīng)用程序和系統(tǒng)界面的正常運行。

我們會經(jīng)常看到,作為系統(tǒng)級資源管理者的瀏覽器,其抉擇始終是:優(yōu)先保障宿主操作系統(tǒng)的穩(wěn)定性,而非滿足單個網(wǎng)頁的無節(jié)制資源需求。

當 WebGL Context 超出限制,瀏覽器會采取強制措施:丟棄“最近最少使用”的那個 WebGL 上下文,并在控制臺輸出一條警告,如:“WARNING: Too many active WebGL contexts. Oldest context will be lost.”(警告:活動 WebGL 上下文過多。最舊的上下文將被丟棄。)。對于那些未預(yù)料到此行為的應(yīng)用而言,這可能導(dǎo)致災(zāi)難性的渲染失敗,且問題難以追蹤。

wKgZO2j3AKGAUqtDAABCAlbxSW4271.png

對于確實需要大量獨立 3D 視圖的應(yīng)用(例如建筑設(shè)計軟件、多視圖監(jiān)控面板),必須采用更高級的架構(gòu)模式來規(guī)避此限制。常見的解決方案推薦復(fù)用 gl context,切換場景的時候,做 clear + dispose 操作清空,并使用同一個 g3d 進行反序列化。

貼圖,buffer 等 GPU 資源對象

在 WebGL 環(huán)境中,代表 GPU 資源的 JavaScript 對象(例如 WebGLTexture 對象),其生命周期與該資源在 GPU 顯存中實際占用的內(nèi)存的生命周期是完全分離的。簡單地將 JavaScript 對象的引用設(shè)置為 null,或讓其離開作用域而被垃圾回收,也不會觸發(fā) GPU 顯存的釋放

WebGL API 劃定了一條清晰的界線:JavaScript 的 WebGLTexture 對象僅僅是一個輕量級的句柄(handle),本質(zhì)上是一個整數(shù) ID。JS GC 可以安全地回收這個句柄對象,且不會對 GPU 產(chǎn)生任何影響。而真正占用顯存(VRAM)的重量級 GPU 資源,唯有開發(fā)者——這個唯一掌握渲染邏輯上下文的角色——顯式調(diào)用對應(yīng)的刪除函數(shù)時,才會被徹底釋放。因此,一旦某個 GPU 資源不再需要,就必須立即調(diào)用對應(yīng)的刪除函數(shù),例如:

gl.deleteTexture()

gl.deleteBuffer()

gl.deleteRenderbuffer()

gl.deleteFramebuffer()

gl.deleteProgram()

gl.deleteShader()

一個標準的 WebGL 資源生命周期應(yīng)遵循“創(chuàng)建-綁定-使用-解綁-刪除”的模式。GPU 顯存泄漏并非瀏覽器的“缺陷”,而是開發(fā)者未能遵守這一顯式契約的結(jié)果。

HT 中的 graph3dView 提供了專門的 dispose 方法,當 3D 場景確定要釋放的時候,主動調(diào)用 g3d.dispose() 將會徹底把當前的所有跟 WebGL 相關(guān)的 GPU 資源徹底釋放。

查看這類資源占用,通常需要觀察系統(tǒng)顯卡的顯存使用情況。以 Windows 系統(tǒng)為例,可以通過:「任務(wù)管理器 → 性能 → GPU → 專用 GPU 內(nèi)存」這一路徑,直觀地看到顯存占用的變化趨勢。以一個 6G 顯存的 GPU 為例,盡量將顯存占用控制在合理范圍(譬如 5G 以內(nèi),避免超過 5.5G),否則一旦超標,系統(tǒng)可能會強制回收顯存資源。

wKgZPGj3AKGACiuxAACQkJcmf1o594.png

原生堆:理解 Blink 的 Oilpan GC

分析完應(yīng)用層的內(nèi)存問題,我們的視線將最終聚焦于瀏覽器的 C++ 底層核心 ——Blink 渲染引擎。

Blink Oilpan GC 是 Chromium 瀏覽器引擎 Blink 中用于管理 C++ 對象內(nèi)存的垃圾回收 (Garbage Collection, GC) 系統(tǒng)。Oilpan 采用的是一種先進的并發(fā)標記與增-量清除 (Concurrent Marking and Incremental Sweeping) 垃圾回收機制。這種機制的核心思想是盡可能地將垃圾回收的工作與主線程 (main thread) 的任務(wù)(例如 JavaScript 執(zhí)行、頁面布局和渲染)并行處理,從而最大限度地減少因 GC 而導(dǎo)致的頁面卡頓 (jank)。

通常這塊內(nèi)存由 Blink 底層管理,Web 應(yīng)用層是無法干預(yù)的,這里我們通過一個實際案例來展開說明:JS 堆快照顯示其 24 小時動畫運行后內(nèi)存增長微乎其微,但 Windows 資源監(jiān)視器卻顯示 Chrome 進程占用了 4GB 內(nèi)存。這種懸殊的差距,讓人不禁好奇。

在 Chrome 地址欄輸入 chrome://tracing 并訪問

點擊頁面中的 "Record" 按鈕,進入錄制配置界面

選擇 "Manually select settings" 選項

點擊 "Edit categories" 按鈕,打開配置列表

在彈出的類別列表中,務(wù)必勾選 memory-infra。

點擊 "OK" 確認配置后,再次點擊 "Record" 開始錄制。等待一段時間后點擊結(jié)束

錄制結(jié)束后,點擊鍵盤的 M 鍵查看具體的內(nèi)存快照

wKgZO2j3AKKACkA8AABBVDXAKMU010.pngwKgZPGj3AKKARPa5AAI7kLW9mkE248.png

從上圖可見,blink_gc 占用 4GB 內(nèi)存,這可能并非內(nèi)存泄漏,而是 Blink 的 Oilpan GC 策略導(dǎo)致的正常現(xiàn)象。其核心機制是內(nèi)存池化 (Memory Pooling):Blink 會預(yù)先向操作系統(tǒng)申請大塊的連續(xù)內(nèi)存區(qū)域。頁面中幾乎所有的 Blink C++ 對象(包括大量臨時的字符串、數(shù)組等)都在這個大內(nèi)存池中進行分配,以提升效率。

當這些短生命周期的對象不再被引用時,它們在邏輯上被視為“垃圾”,但它們所占用的物理內(nèi)存并不會立即歸還給操作系統(tǒng)。GC 回收器會根據(jù)當前的內(nèi)存壓力 (Memory Pressure) 來決定何時執(zhí)行徹底的清理。

在本案例中,機器總內(nèi)存高達 64GB,資源充裕,Chrome 判斷無需迫切回收。為避免不必要的性能開銷(一次完整的 GC 會消耗 CPU 資源),GC 選擇推遲回收操作。因此,我們看到的 4GB 占用,實際上是 Oilpan GC 持有的一個較大的內(nèi)存池,其中包含了活動對象和大量待回收的“垃圾”對象。只要這個內(nèi)存池的大小趨于穩(wěn)定,沒有出現(xiàn)持續(xù)、無節(jié)制的增長,通常就不構(gòu)成內(nèi)存泄漏問題。

HT 與內(nèi)存泄露

綜合上述的三部分內(nèi)容,我們捋清了內(nèi)存泄漏問題的主要原因,并掌握了對應(yīng)的排查方法。而在 HT 框架中,內(nèi)存泄漏的問題在 3D 場景中最為常見,由于 HT 的 3D 是基于 WebGL 實現(xiàn)的,此類泄漏往往會表現(xiàn)得尤為明顯。

為了清晰呈現(xiàn) HT 3D 中的內(nèi)存泄漏問題,我們設(shè)計了一個簡單的對照實現(xiàn)來進行演示。

對照實驗

在展開實驗前,我們先簡要了解下 HT 框架的核心架構(gòu)。HT 采用 MV 架構(gòu)模式,在 HT 的框架設(shè)計中, Data 模型和 View 視圖是分離的,二者之間通過 Event 事件監(jiān)聽和派發(fā)機制來建立起數(shù)據(jù)綁定。

wKgZO2j3AKOAI7s1AAFSAQgE3Qk217.png

在實驗操作前,我們可以打開 Chrome DevTools -> Performance (性能) 面板,并且點擊面板中的錄制按鈕,記錄整個實驗過程,這能幫助我們在操作結(jié)束后,回溯并分析全程性能和內(nèi)存的變化情況。

wKgZPGj3AKOABbE1AADbopb4ivo854.png

實驗環(huán)境:

瀏覽器:Chrome 138.0.7204.101(64位)

顯卡:NVIDIA GeForce GTX 1660 Ti

處理器:Intel(R) Core(TM) i5-10400F CPU @ 2.90GHz (2.90 GHz)

第一次實驗

我們通過按鈕不斷創(chuàng)建新的視圖,當頁面中超過一定數(shù)量Graph3dView 時,可以看到第一個場景“崩潰”,但是當我們刪除最后一個 Graph3dView 后,第一個場景又恢復(fù)了。

wKgZO2j3AKSAbTyIACDXRLrmPRI263.gif

我們可以從 Performance 面板中觀察到整個過程:

當首個 WebGL 上下文被銷毀后,JS 堆內(nèi)存出現(xiàn)明顯下降。

刪除最新的視圖后,首個 WebGL 恢復(fù),且刪除后事件監(jiān)聽器占用的內(nèi)存下降

由于 HT 是 MV 框架,雖然瀏覽器銷毀了 WebGL 上下文,但是視圖的數(shù)據(jù)模型仍然保留,這也就是首個視圖“復(fù)活”的原因。

wKgZPGj3AKWAUIHXAAD9XtdaLek636.png

第二次實驗

我們將所有的 Graph3dView 都綁定到一個 window.dataModel 上。具體可以參考下圖:

wKgZO2j3AKaATZApAAC0k7ak7-g065.png

同實驗一,我們也通過按鈕創(chuàng)建多個視圖,在告警后刪除最后一個視圖。可以發(fā)現(xiàn),當刪除了最后一個 Graph3dView ,第一個場景也并沒有恢復(fù)。

wKgZO2j3AKaAMm_RAAK8zSPyfLA380.gif

我們從 Performance 面板中觀察到整個過程:

當首個 WebGL 上下文被銷毀后,JS 堆內(nèi)存出現(xiàn)明顯下降

刪除最新的視圖后,首個 WebGL 沒有恢復(fù),且刪除后事件監(jiān)聽器占用的內(nèi)存也沒有出現(xiàn)下降的情況

照第一組實驗的結(jié)論來說,只要數(shù)據(jù)模型還在,視圖應(yīng)當“復(fù)活”,但是視圖并沒有“復(fù)活”。

wKgZPGj3AKeAB4k0AADcl-o6YXw484.png

為什么會出現(xiàn)上述兩種情況?這是因為第二次,并沒有正確地將 Graph3dView 清除。可以看一下兩次實驗系統(tǒng)的內(nèi)存對象引用關(guān)系。

第一次

wKgZO2j3AKeAKWxFAAJtfbeX1bM883.pngwKgZPGj3AKiAM6s6AAGkItz4ga8531.png

第二次

第一次實驗,頁面上有 19 個 Graph3dView,在內(nèi)存中看到有 19 個 Graph3dView 對象,而第二次頁面上僅有 7 個 Graph3dView,但是內(nèi)存中有 17 個 Graph3dView 對象。這就說明了第二次的 Graph3dView 并沒有被正確垃圾回收,這也就導(dǎo)致了即使移除了一個 Graph3dView,第一個場景也并不會恢復(fù)。打開其中一個 Graph3dView 可以看到,Graph3dView 與 window.dataModel 存在引用關(guān)系導(dǎo)致的。

wKgZO2j3AKiAIwL9AAHo52iySb0358.png

解決方案

從上述的對照試驗中,可以看出使用全局變量存儲視圖實例是導(dǎo)致內(nèi)存泄漏的主要原因。當多個 Graph3dView 共享同一個全局 dataModel 時,即使刪除視圖,由于全局引用依然存在,這些視圖無法被垃圾回收。

針對于內(nèi)存泄漏可以通過以下幾個方案解決:

1、避免全局變量引用

該方案從業(yè)務(wù)架構(gòu)層面上解決內(nèi)存泄漏問題,可采用以下實現(xiàn)方式:

使用模塊化設(shè)計代替全局變量存儲

采用弱引用等機制管理視圖對象

建立專門的視圖管理器統(tǒng)一管理實例

2、視圖復(fù)用機制

從實驗上可以看出,頻繁創(chuàng)建和銷毀視圖會帶來顯著的性能損耗。在實際的業(yè)務(wù)場景中,可以通過復(fù)用視圖來提升性能。在切換視圖時,僅需要通過 dataModel.clear() 清空數(shù)據(jù)模型,重新對視圖進行反序列化即可。

3、資源釋放

對于必須要頻繁創(chuàng)建/銷毀視圖的特殊場景,可在銷毀前執(zhí)行以下操作:

const dm = new ht.DataModel();

view.setDataModel(dm);

view.dispose();

關(guān)鍵要點:

創(chuàng)建新的 dataModel 實例替換原有引用

有效解除視圖與業(yè)務(wù)數(shù)據(jù)的關(guān)聯(lián)關(guān)系

3D 視圖上存在 dispose 方法,用于主動釋放 gl 的資源

需要注意:在具體的項目中,優(yōu)先考慮上兩個方案,此方案適用于必須銷毀視圖的特殊情況。

wKgZPGj3AKmAKllyAAi4xZcVw20416.gif

4、事件管理優(yōu)化

在處理模塊通信上,可以考慮使用 HT 的事件派發(fā)器進行。項目全局上創(chuàng)建一個事件派發(fā)器,模塊間消息傳遞使用派發(fā)器進行:

const notifier = new ht.Notifier();

const func = function(e) {}

notifier.add(func); // 添加監(jiān)聽函數(shù)

notifier.remove(func); // 刪除監(jiān)聽函數(shù)

notifier.fire(func); // 派發(fā)事件

關(guān)鍵要點:

統(tǒng)一使用事件派發(fā)器進行跨模塊通信,避免不同模塊間的直接調(diào)用、依賴,減少內(nèi)存泄漏風(fēng)險

若模塊需要銷毀,在銷毀前需移除相關(guān)事件監(jiān)聽

必須使用具名函數(shù)而非匿名函數(shù)作為事件處理器

在前端開發(fā)過程中,開發(fā)者應(yīng)持續(xù)關(guān)注內(nèi)存變化。內(nèi)存泄漏問題并非都是 “爆發(fā)式顯現(xiàn)”,更多是 “漸進式累積” —— 初期往往難以察覺,但隨著時間推移,過高的內(nèi)存占用會直接拖慢運行性能;對于基于 WebGL 的應(yīng)用,甚至可能引發(fā)上下文丟失、頁面白屏等嚴重問題。因此,對待內(nèi)存泄漏,我們必須保持常態(tài)化關(guān)注的心態(tài)。

審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • Canvas
    +關(guān)注

    關(guān)注

    0

    文章

    21

    瀏覽量

    11428
  • 內(nèi)存泄露
    +關(guān)注

    關(guān)注

    0

    文章

    7

    瀏覽量

    2165
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點推薦

    基于 HT 引擎零代碼搭建 3D 智慧農(nóng)場,實現(xiàn)耕種管收全無人

    基于 HT 前端可視化插件,提出 3D 智慧農(nóng)場可視化解決方案。方案采用 HTML5、WebGLCanvas 技術(shù),結(jié)合 WebSocket/HTTP 協(xié)議,實現(xiàn)農(nóng)業(yè)生產(chǎn)全環(huán)節(jié)的智能化管理。系統(tǒng)
    的頭像 發(fā)表于 03-05 15:34 ?42次閱讀
    基于 HT 引擎零代碼搭建 3D 智慧農(nóng)場,實現(xiàn)耕種管收全無人

    基于圖撲 HT 引擎:數(shù)字孿生民航飛聯(lián)網(wǎng)方案

    圖撲基于 HTML5 自主研發(fā) 2D、3D 圖形渲染引擎,依托 WebGLCanvas 技術(shù)棧打造純前端可視化插件 HT for Web。該插件支持輕量化三維模型導(dǎo)入加載,可完成界面渲染、組件聯(lián)動
    的頭像 發(fā)表于 02-05 14:26 ?99次閱讀
    基于圖撲 HT 引擎:數(shù)字孿生民航飛聯(lián)網(wǎng)方案

    基于圖撲 HT 數(shù)字孿生 3D 風(fēng)電場可視化系統(tǒng)實現(xiàn)解析

    了數(shù)字孿生 3D 風(fēng)電場可視化系統(tǒng),實現(xiàn)了風(fēng)電場全場景的遠程監(jiān)測、智能巡檢與數(shù)字化管理。本文從技術(shù)角度出發(fā),結(jié)合系統(tǒng)功能模塊,深入解析各核心功能的實現(xiàn)邏輯與技術(shù)路徑。 系統(tǒng)以 HT for Web 為核心技術(shù)支撐,該引擎基于 WebGLCanvas 技術(shù)構(gòu)建,具備高
    的頭像 發(fā)表于 01-09 15:35 ?321次閱讀
    基于圖撲 HT 數(shù)字孿生 3D 風(fēng)電場可視化系統(tǒng)實現(xiàn)解析

    RTOS Crash 問題全維度分析與解決指南

    的memheap/memstat、FreeRTOS的xPortGetFreeHeapSize(),實時打印堆剩余空間,定位內(nèi)存泄露; 任務(wù)調(diào)度跟蹤 :通過邏輯分析儀/串口打印任務(wù)切換日志(如“TaskA
    發(fā)表于 12-08 03:56

    基于HT實現(xiàn)海上LNG終端數(shù)字孿生可視化監(jiān)控

    在能源轉(zhuǎn)型與雙碳目標的推動下,液化天然氣(LNG)終端的智能化運維成為行業(yè)發(fā)展的核心需求。圖撲軟件依托自研的 HT for Web 前端插件(基于 WebGLCanvas 技術(shù)開發(fā)),結(jié)合數(shù)字孿生
    的頭像 發(fā)表于 12-01 17:41 ?603次閱讀
    基于HT實現(xiàn)海上LNG終端數(shù)字孿生可視化監(jiān)控

    圖撲 HT 驅(qū)動智慧社區(qū)數(shù)字化轉(zhuǎn)型:多維可視化與系統(tǒng)集成實踐

    在社區(qū)管理向數(shù)字化、智能化升級的浪潮中,圖撲軟件(Hightopo)依托自主研發(fā)的HT for Web 前端可視化技術(shù),構(gòu)建起覆蓋社區(qū)全場景的數(shù)字孿生智慧社區(qū)解決方案。該方案以 WebGL
    的頭像 發(fā)表于 10-31 14:44 ?465次閱讀
    圖撲 HT 驅(qū)動智慧社區(qū)數(shù)字化轉(zhuǎn)型:多維可視化與系統(tǒng)集成實踐

    圖撲 HT 技術(shù)賦能智慧畜牧三維可視化:架構(gòu)設(shè)計與實踐應(yīng)用

    在現(xiàn)代農(nóng)業(yè)數(shù)字化轉(zhuǎn)型浪潮中,智慧畜牧作為畜牧業(yè)升級的核心方向,正通過信息技術(shù)重構(gòu)養(yǎng)殖管理模式。圖撲軟件(Hightopo)基于自主研發(fā)的 HT for Web 技術(shù),以 WebGLCanvas
    的頭像 發(fā)表于 09-19 14:48 ?550次閱讀
    圖撲 HT 技術(shù)賦能智慧畜牧三維可視化:架構(gòu)設(shè)計與實踐應(yīng)用

    【M-K1HSE開發(fā)板免費體驗】相關(guān)源碼分析與實現(xiàn)2-全部案例運行一般思路

    直接在內(nèi)存中操作的位圖對象,可以被 Canvas 用來繪制。 定時器 (Timer): 代碼中雖然沒有直接展示 setInterval,但 TimeChangeListener 的實現(xiàn)原理必然是基于一
    發(fā)表于 09-03 21:46

    基于 HT 可視化實現(xiàn)三維物流園區(qū)一體化管控系統(tǒng)

    軟件 HT for Web 技術(shù),依托 WebGLCanvas 等底層技術(shù),構(gòu)建了集 2D 組態(tài)與 3D 仿真于一體的數(shù)字孿生系統(tǒng),為物流園區(qū)的全場景管控提供了技術(shù)底座。
    的頭像 發(fā)表于 08-07 17:07 ?731次閱讀
    基于 HT 可視化實現(xiàn)三維物流園區(qū)一體化管控系統(tǒng)

    HT 可視化在工業(yè)產(chǎn)線看板智能化應(yīng)用中的技術(shù)實現(xiàn)

    在工業(yè)數(shù)字化轉(zhuǎn)型進程中,傳統(tǒng)生產(chǎn)管理模式因依賴人工采集數(shù)據(jù)、信息傳遞滯后等問題,已難以滿足高效生產(chǎn)需求。圖撲軟件的 HT for Web 產(chǎn)品依托 WebGLCanvas 等前端技術(shù),為工業(yè)產(chǎn)線
    的頭像 發(fā)表于 07-25 15:10 ?568次閱讀
    HT 可視化在工業(yè)產(chǎn)線看板智能化應(yīng)用中的技術(shù)實現(xiàn)

    【HarmonyOS next】ArkUI-X休閑益智消消樂【進階】

    Canvas批次繪制使幀率穩(wěn)定≥55FPS 內(nèi)存控制:對象復(fù)用使內(nèi)存占用≤35MB 啟動加速:本地資源加載速度<0.3s 六、開發(fā)實踐建議 通信優(yōu)化:復(fù)雜數(shù)據(jù)交互使用WebView.postMessage
    發(fā)表于 06-28 21:59

    基于 HT 2D&3D 渲染引擎的新能源充電樁可視化運營系統(tǒng)技術(shù)剖析

    在新能源汽車產(chǎn)業(yè)快速發(fā)展的浪潮中,充電樁作為關(guān)鍵配套設(shè)施,其運營管理的高效性和智能化愈發(fā)重要。圖撲軟件憑借基于 WebGLCanvas 的 HT 2D3D 渲染引擎依托 WebGL
    的頭像 發(fā)表于 03-20 11:47 ?873次閱讀
    基于 HT 2D&3D 渲染引擎的新能源充電樁可視化運營系統(tǒng)技術(shù)剖析

    分享之前使用HarmonyOS NEXT Canvas做的動態(tài)GIF視頻的一個案例,沒有感情,全是技術(shù)。

    。這次分享一下之前使用HarmonyOS NEXT Canvas做的動態(tài)視頻的一個案例,沒有感情,全是技術(shù)。 開發(fā)準備 開發(fā)流程與進度 這次整體開發(fā)流程主要如下: 獲取圖片素材列表數(shù)據(jù),初始化視頻的幀數(shù)以及canvas畫布 繪畫視頻控制器,編寫視頻按幀數(shù)播放的功能 動態(tài)切
    的頭像 發(fā)表于 03-16 15:56 ?981次閱讀