最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

前端檢查內(nèi)存泄露完整示例

 更新時間:2026年05月03日 10:59:35   作者:遺憾隨她而去.  
內(nèi)存泄漏是指應(yīng)用程序中的內(nèi)存被錯誤地分配和釋放,導(dǎo)致無法再次使用的內(nèi)存堆積,這通常是由于開發(fā)者忘記釋放不再使用的對象或數(shù)據(jù)而引起的,這篇文章主要介紹了前端檢查內(nèi)存泄露的相關(guān)資料,需要的朋友可以參考下

前言

前端應(yīng)用的內(nèi)存泄露,指不再使用內(nèi)存未被釋放,導(dǎo)致頁面占用內(nèi)存持續(xù)增長,輕則引發(fā)頁面卡頓,加載緩慢,重則導(dǎo)致瀏覽器崩潰, 尤其在單頁應(yīng)用SPA中,路由切換頻繁但內(nèi)存不回收,問題會被無限放大,比如用戶長時間使用某后臺管理系統(tǒng),可能出現(xiàn)操作響應(yīng)式延遲,甚至需要強制刷新才能恢復(fù),這很可能是內(nèi)存泄露在"作祟"

一. 前端常見的內(nèi)存泄露場景

1. 意外的全局變量:未聲明的變量(如a = 10而非let a = 10)會掛載到window上,頁面不刷新就不會釋放;

2. 閉包濫用:閉包會保留對外部作用域的引用,若長期持有 DOM 或大型對象,會導(dǎo)致內(nèi)存無法回收(如未清理的事件監(jiān)聽回調(diào)) ;

3. 未清理的 DOM 引用:刪除 DOM 節(jié)點后,仍保留其引用(如let el =       document.getElementById('test'),刪除el對應(yīng)的 DOM 后未置el = null);

4. 定時器 / 事件監(jiān)聽未銷毀:setInterval未用clearInterval清除、addEventListener未用removeEventListener解綁,尤其在組件掛載 / 卸載時容易遺漏;

5.第三方庫 / 插件殘留:部分第三方庫(如圖表庫、播放器)使用后未調(diào)用銷毀方法,導(dǎo)致內(nèi)部資源無法釋放;

6.數(shù)組 / 對象無限增長:全局緩存對象未設(shè)置過期機制,或數(shù)組持續(xù) push 數(shù)據(jù)但未清理無效項。

二. 核心工具: 瀏覽器開發(fā)者工具(Chrome DevTools )

1.第一步: 復(fù)現(xiàn)內(nèi)存泄露場景

首先需要穩(wěn)定復(fù)現(xiàn)泄露行為,比如: 

路由切換(SPA 中反復(fù)切換 A→B→A→B);

點擊按鈕觸發(fā)某操作(如打開彈窗后關(guān)閉);

長時間滾動或輪詢請求數(shù)據(jù)。

2.第二步

1. 打開Chrome 開發(fā)者工具

2. 勾選面板頂部的「Memory」選項(同時可保留默認的「CPU」「Network」);

3.點擊左上角的「錄制」按鈕(圓形紅點),然后執(zhí)行復(fù)現(xiàn)步驟(如切換路由 5 次);

4. 執(zhí)行完成后點擊「停止」,等待面板生成報告。

關(guān)鍵觀察點: 

報告中[Memory]曲線若持續(xù)上升且不回落(即使操作停止后,內(nèi)存仍未下降),則大概率存在泄露

若曲線在操作后能回落至初始水平,說明內(nèi)存已正?;厥?無泄露

3.第三步

Memory 面板定位泄露對象,主要是用于記錄頁面堆內(nèi)存的具體情況以及js堆內(nèi)存隨加載時間線動態(tài)的分配情況

Performance 面板確認泄露后,用 Memory 面板進一步定位 “哪些對象未被回收”:

1. 常用快照類型

Memory面板支持3中快照類型,按需選擇:

  • Heap snapshot(堆快照):捕獲當前內(nèi)存中所有對象的快照,可對比不同快照的差異,適合定位未回收的對象;
  • Allocation instrumentation on timeline(時間線分配記錄):實時記錄內(nèi)存分配過程,適合觀察某操作期間的內(nèi)存分配細節(jié);
  • Allocation sampling(分配采樣):低開銷的內(nèi)存采樣,適合快速排查大面積泄露,無需精確到單個對象。

2. 堆快照對比操作

  • 打開 DevTools → 切換到「Memory」面板;
  • 選擇「Heap snapshot」,點擊「Take snapshot」拍攝初始快照(記為快照 1,此時未執(zhí)行任何操作);
  • 執(zhí)行一次泄露復(fù)現(xiàn)步驟(如切換一次路由);
  • 再次點擊「Take snapshot」拍攝快照 2;
  • 重復(fù)復(fù)現(xiàn)步驟 N 次(如再切換 3 次路由),拍攝快照 3;
  • 在 Memory 面板左側(cè)的快照列表中,點擊快照 2/3 的下拉框,選擇「Comparison」(對比),并選擇 “與快照 1 對比”; (下圖對比我用的是 5 和 6 快照進行對比)

這時候就能看出變占用的內(nèi)存大小,是那些操作造成了內(nèi)存泄露

堆快照就像照相機一樣,能記錄你當前頁面的堆內(nèi)存情況,每快照一次就會產(chǎn)生一條快照記錄

如上圖所示,剛開始執(zhí)行了一次快照,記錄了當時堆內(nèi)存空間占用為59.4MB,然后我們點擊了頁面中某些按鈕,又執(zhí)行一次快照,記錄了當時堆內(nèi)存空間占用為59.7MB。并且點擊對應(yīng)的快照記錄,能看到當時所有內(nèi)存中的變量情況(結(jié)構(gòu)、占總占用內(nèi)存的百分比...)

在開始記錄后,我們可以看到圖中右上角有起伏的藍色與灰色的柱形圖,其中藍色表示當前時間線下占用著的內(nèi)存; 灰色表示之前占用的內(nèi)存空間已被清除釋放

用Allocation on timeline 

三. 示例

1. 閉包使用不當引起內(nèi)存泄漏

使用Performance和Memory來查看一下閉包導(dǎo)致的內(nèi)存泄漏問題在退出fn1函數(shù)執(zhí)行上下文后,該上下文中的變量a本應(yīng)被當作垃圾數(shù)據(jù)給回收掉,但因fn1函數(shù)最終將變量a返回并賦值給全局變量res,其產(chǎn)生了對變量a的引用,所以變量a被標記為活動變量并一直占用著相應(yīng)的內(nèi)存,假設(shè)變量res后續(xù)用不到,這就算是一種閉包使用不當?shù)睦?/p>

<button onclick="myClick()">執(zhí)行fn1函數(shù)</button>
<script>
function fn1 () {
let a = new Array(10000) // 這里設(shè)置了一個很大的數(shù)組對象
let b = 3
function fn2() {
let c = [1, 2, 3]
}
fn2()
return a
}
let res = []
function myClick() {
res.push(fn1())
}
</script>

設(shè)置了一個按鈕,每次執(zhí)行就會將fn1函數(shù)的返回值添加到全局數(shù)組變量res中,是為了能在performacne的曲線圖中看出效果 

使用Performance和Memory來查看一下閉包導(dǎo)致的內(nèi)存泄漏問題

錄制的時候去執(zhí)行fn1 函數(shù)

  • 在每次錄制開始時手動觸發(fā)一次垃圾回收機制,這是為了確認一個初始的堆內(nèi)存基準線,便于后面的對比,然后我們點擊了幾次按鈕,即往全局數(shù)組變量res中添加了幾個比較大的數(shù)組對象,最后再觸發(fā)一次垃圾回收,發(fā)現(xiàn)錄制結(jié)果的JS Heap曲線剛開始成階梯式上升的,最后的曲線的高度比基準線要高,說明可能是存在內(nèi)存泄漏的問題
  • 在得知有內(nèi)存泄漏的情況存在時,我們可以改用Memory來更明確得確認問題和定位問題
  • 首先可以用Allocation instrumentation on timeline來確認問題,如下圖所示:

先選擇這個 

然后去錄制 

在我們每次點擊按鈕后,動態(tài)內(nèi)存分配情況圖上都會出現(xiàn)一個藍色的柱形,并且在我們觸發(fā)垃圾回收后,藍色柱形都沒變成灰色柱形,即之前分配的內(nèi)存并未被清除,即之前分配的內(nèi)存并未被清除

所以此時我們就可以更明確得確認內(nèi)存泄露的問題是存在的了,,接下來就精準定位問題,可以利用Heap snapshot來定位問題,如圖所示:

  • 第一次先點擊快照記錄初始的內(nèi)存情況,然后我們多次點擊按鈕后再次點擊快照,記錄此時的內(nèi)存情況,發(fā)現(xiàn)從原來的1.2 M內(nèi)存空間變成了1.7 M內(nèi)存空間,然后我們選中第二條快照記錄,可以看到右上角有個All objects的字段,其表示展示的是當前選中的快照記錄所有對象的分配情況,而我們想要知道的是第5條快照與第一條快照的區(qū)別在哪,所以選擇Object allocated between Snapshot1 and Snapshot2即展示第一條快照和第二條快照存在差異的內(nèi)存對象分配情況,此時可以看到Array的百分比很高,初步可以判斷是該變量存在問題,點擊查看詳情后就能查看到該變量對應(yīng)的具體數(shù)據(jù)了

以上就是一個判斷閉包帶來內(nèi)存泄漏問題并簡單定位的方法了

2.全局變量

全局的變量一般是不會被垃圾回收掉的當然這并不是說變量都不能存在全局,只是有時候會因為疏忽而導(dǎo)致某些變量流失到全局,例如未聲明變量,卻直接對某變量進行賦值,就會導(dǎo)致該變量在全局創(chuàng)建,如下所示:

function fn1() {
// 此處變量name未被聲明
name = new Array(99999999)
}
fn1()
  • 此時這種情況就會在全局自動創(chuàng)建一個變量name,并將一個很大的數(shù)組賦值給name,又因為是全局變量,所以該內(nèi)存空間就一直不會被釋放
  • 解決辦法的話,自己平時要多加注意,不要在變量未聲明前賦值,或者也可以開啟嚴格模式,這樣就會在不知情犯錯時,收到報錯警告,例如

3.分離的DOM節(jié)點

假設(shè)你手動移除了某個dom節(jié)點,本應(yīng)釋放該dom節(jié)點所占用的內(nèi)存,但卻因為疏忽導(dǎo)致某處代碼仍對該被移除節(jié)點有引用,最終導(dǎo)致該節(jié)點所占內(nèi)存無法被釋放,例如這種情況

<div id="root">
<div class="child">我是子元素</div>
<button>移除</button>
</div>
<script>
let btn = document.querySelector('button')
let child = document.querySelector('.child')
let root = document.querySelector('#root')
btn.addEventListener('click', function() {
root.removeChild(child)
})
</script>

該代碼所做的操作就是點擊按鈕后移除.child的節(jié)點,雖然點擊后,該節(jié)點確實從dom被移除了,但全局變量child仍對該節(jié)點有引用,所以導(dǎo)致該節(jié)點的內(nèi)存一直無法被釋放,可以嘗試用Memory的快照功能來檢測一下,如圖所示

同樣的先記錄一下初始狀態(tài)的快照,然后點擊移除按鈕后,再點擊一次快照,此時內(nèi)存大小我們看不出什么變化,因為移除的節(jié)點占用的內(nèi)存實在太小了可以忽略不計,但我們可以點擊第二條快照記錄,在篩選框里輸入detached,于是就會展示所有脫離了卻又未被清除的節(jié)點對象

解決辦法如下圖所示:

<div id="root">
<div class="child">我是子元素</div>
<button>移除</button>
</div>
<script>
let btn = document.querySelector('button')

btn.addEventListener('click', function() {
let child = document.querySelector('.child')
let root = document.querySelector('#root')

root.removeChild(child)
})

</script>

改動很簡單,就是將對.child節(jié)點的引用移動到了click事件的回調(diào)函數(shù)中,那么當移除節(jié)點并退出回調(diào)函數(shù)的執(zhí)行上文后就會自動清除對該節(jié)點的引用,那么自然就不會存在內(nèi)存泄漏的情況了,我們來驗證一下,如下圖所示:

結(jié)果很明顯,這樣處理過后就不存在內(nèi)存泄露的情況了 

4. 控制臺打印

<button>按鈕</button>
<script>
document.querySelector('button').addEventListener('click', function() {
let obj = new Array(1000000)
console.log(obj);
})
</script>

我們在按鈕的點擊回調(diào)事件中創(chuàng)建了一個很大的數(shù)組對象并打印,用performance來驗證一下

開始錄制,先觸發(fā)一次垃圾回收清除初始的內(nèi)存,然后點擊三次按鈕,即執(zhí)行了三次點擊事件,最后再觸發(fā)一次垃圾回收。查看錄制結(jié)果發(fā)現(xiàn)JS Heap曲線成階梯上升,并且最終保持的高度比初始基準線高很多,這說明每次執(zhí)行點擊事件創(chuàng)建的很大的數(shù)組對象obj都因為console.log被瀏覽器保存了下來并且無法被回收

接下來注釋掉console.log,再來看一下結(jié)果

<button>按鈕</button>
<script>
document.querySelector('button').addEventListener('click', function() {
let obj = new Array(1000000)
// console.log(obj);
})
</script>

可以看到?jīng)]有打印以后,每次創(chuàng)建的obj都立馬被銷毀了,并且最終觸發(fā)垃圾回收機制后跟初始的基準線同樣高,說明已經(jīng)不存在內(nèi)存泄漏的現(xiàn)象了

5.遺忘的定時器

定時器也是平時很多人會忽略的一個問題,比如定義了定時器后就再也不去考慮清除定時器了,這樣其實也會造成一定的內(nèi)存泄漏。來看一個代碼示例:

<button>開啟定時器</button>
<script>
function fn1() {
let largeObj = new Array(100000)
setInterval(() => {
let myObj = largeObj
}, 1000)
}
document.querySelector('button').addEventListener('click', function() {
fn1()
})
</script>

這段代碼是在點擊按鈕后執(zhí)行fn1函數(shù),fn1函數(shù)內(nèi)創(chuàng)建了一個很大的數(shù)組對象largeObj,同時創(chuàng)建了一個setInterval定時器,定時器的回調(diào)函數(shù)只是簡單的引用了一下變量largeObj,我們來看看其整體的內(nèi)存分配情況吧:

按道理來說點擊按鈕執(zhí)行fn1函數(shù)后會退出該函數(shù)的執(zhí)行上下文,緊跟著函數(shù)體內(nèi)的局部變量應(yīng)該被清除,但圖中performance的錄制結(jié)果顯示似乎是存在內(nèi)存泄漏問題的,即最終曲線高度比基準線高度要高,那么再用Memory來確認一次:

  • 在我們點擊按鈕后,從動態(tài)內(nèi)存分配的圖上看到出現(xiàn)一個藍色柱形,說明瀏覽器為變量largeObj分配了一段內(nèi)存,但是之后這段內(nèi)存并沒有被釋放掉,說明的確存在內(nèi)存泄漏的問題,原因其實就是因為setInterval的回調(diào)函數(shù)內(nèi)對變量largeObj有一個引用關(guān)系,而定時器一直未被清除,所以變量largeObj的內(nèi)存也自然不會被釋放
  • 那么我們?nèi)绾蝸斫鉀Q這個問題呢,假設(shè)我們只需要讓定時器執(zhí)行三次就可以了,那么我們可以改動一下代碼:
<button>開啟定時器</button>
<script>
function fn1() {
let largeObj = new Array(100000)
let index = 0

let timer = setInterval(() => {
if(index === 3) clearInterval(timer);
let myObj = largeObj
index ++
}, 1000)
}

document.querySelector('button').addEventListener('click', function() {
fn1()
})
</script>

現(xiàn)在我們再通過performance和memory來看看還不會存在內(nèi)存泄漏的問題

  • performance

  • memory
  • 后我們點擊了按鈕,看到又出現(xiàn)了一個藍色柱形,此時就是為fn1函數(shù)中的變量largeObj分配了內(nèi)存,3s后該內(nèi)存又被釋放了,即變成了灰色柱形。所以我們可以得出結(jié)論,這段代碼不存在內(nèi)存泄漏的問題

總結(jié) 

到此這篇關(guān)于前端檢查內(nèi)存泄露的文章就介紹到這了,更多相關(guān)前端檢查內(nèi)存泄露內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

苏尼特左旗| 睢宁县| 达尔| 玉环县| 镇江市| 定西市| 枣庄市| 外汇| 文山县| 海口市| 河西区| 瑞金市| 石景山区| 清新县| 施甸县| 商都县| 湖口县| 赤水市| 霍城县| 哈尔滨市| 南靖县| 忻城县| 江津市| 营山县| 定西市| 双江| 东乡县| 长丰县| 嘉祥县| 平邑县| 江门市| 邓州市| 贵溪市| 肥东县| 鞍山市| 万荣县| 库车县| 汕尾市| 商河县| 长岭县| 务川|