【前端--JavaScript】知識點(六)—— 項目中的內存泄漏(二)

這是我參與8月更文挑戰的第6天,活動詳情查看:8月更文挑戰javascript

上一篇中說到,不經意的內存泄露會致使程序變卡,甚至會出現頁面崩潰,下面經過例子來從瀏覽器的角度排查是否存在內存泄露,怎麼定位內存泄露。html

內存泄露排查

例子:java

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>test</title>
</head>
<body>
  <button id="click">click</button>
  <h1 id="content"></h1>
  <script>     let click = document.querySelector("#click");     let content = document.querySelector("#content")     let arr = [];     function testFun({       let test = new Array(1000).fill('1111')       return function ({         return test       }     }     click.addEventListener("click"function ({       arr.push(testFun())       arr.push(testFun())       content.innerHTML = arr.length     });   </script>
</body>
</html>
複製代碼

這是一個不正當使用閉包構成的內存泄露的例子。 代碼中,爲頁面的button綁定了一個點擊事件,每次點擊都執行兩次閉包函數,並把結果push 到全局的數組arr中,因爲閉包函數執行結果也是一個函數,而且存在對原閉包函數內部數組test的引用,因此arr數組中的每一項元素都使得引用的閉包內部test數組對象沒有辦法回收,arr數組有多少元素,也就存在多少次閉包引用,咱們點擊的越多,push就越多,內存就消耗越大,頁面也會愈來愈卡。數組

這是咱們知道問題的狀況下,對內存泄露的排查,當項目龐大的時候咱們就無法直接定位到具體代碼了,這時候就須要藉助瀏覽器控制檯(Chrome Devtool)了。瀏覽器

首先打開瀏覽器的無痕模式,打開程序控制臺:markdown

image.png 而後找到Performance,他是用來監控性能指標的工具,能夠記錄分析在網站的生命週期內發的各種事件:閉包

image.png

接下來開始操做,在開始以前必定要確認勾選了 Memory (序號5)選項 ,這樣咱們才能夠看到內存相關的分析。app

點擊開始錄製(序號 1)進入錄製狀態,隨後先清理一下GC(垃圾回收),也就是最右邊的垃圾桶(序號6)。函數

而後一直點擊按鈕50次,頁面數值應該是100,再點擊一下垃圾桶,手動觸發一次GC(垃圾回收),再點擊150次,而後中止錄製。工具

image.png 咱們能夠明顯看到內存數據是一個不斷上漲的趨勢,並且即便咱們在50次的時候手動垃圾回收了一次,清理後的內存也沒有少不少。

image.png

內存泄露分析定位

在這段代碼中,咱們能夠很快的知道是點擊按鈕的操做出現了內存泄露,但真實項目中,項目過於龐大,操做過多的狀況下,是不容易很快定位到具體問題的。這時候須要藉助Memory,它能夠記錄JS中CPU執行時間細節,顯示JS對象和相關的DOM節點的內存消耗內存的分配細節等。

Memory中的Heap Profiling 能夠記錄當前的堆內存 heap 的快照,並生成對象的描述文件,該描述文件給出了當下 JS 運行所用的全部對象,以及這些對象所佔用的內存大小、引用的層級關係等等,用它就能夠定位出引發問題的具體緣由以及位置。

image.png

咱們點擊左邊的生成快照,就生成了Snapshot 1,它表明了剛剛這一刻的內存狀態。

image.png

快照這邊有一個下拉框:

  • Summary:按照構造函數進行分組,捕獲對象和其使用內存的狀況,可理解爲一個內存摘要,用於跟蹤定位DOM節點的內存泄漏
  • Containment:探測堆的具體內容,提供一個視圖來查看對象結構,有助分析對象引用狀況,可分析閉包及更深層次的對象分析
  • Statistics:統計視圖

默認顯示的是Summary,下面的信息就表明快照生成的一瞬間,內存中存了什麼,佔用內存的信息等。

image.png Summary中數據表格的信息表示什麼:

  • Constructor:顯示全部的構造函數,點擊每個構造函數能夠查看由該構造函數建立的全部對象
  • Distance:顯示經過最短的節點路徑到根節點的距離,引用層級
  • Shallow Size:顯示對象所佔內存,不包含內部引用的其餘對象所佔的內存
  • Retained Size:顯示對象所佔的總內存,包含內部引用的其餘對象所佔的內存

image.png 咱們這時候選擇第三個直接進行對比:

  • system、system/Context 表示引擎本身建立的以及上下文建立的一些引用,這些不用太關注,不重要
  • closure 表示一些函數閉包中的對象引用

image.png

在這裏咱們點開能夠看到,代碼在19行存在內存泄露,同時看Array,也表明着全局變量要是在頁面關閉前沒清理,就會一直存在內存中。

這樣咱們就成功了定位到了內存泄露的緣由。 在實際項目中,問題可能會更加複雜,咱們要根據具體場景選擇解決方案,解決以後重複上面排查流程就行排查。

內存三大件

在這裏順便了解一下什麼是內存三大件,也就是內存方面主要的三個問題:

內存泄漏 咱們說好久了,對象已經再也不使用但沒有被回收,內存沒有被釋放,即內存泄漏,那想要避免就避免讓無用數據還存在引用關係,也就是多注意咱們上面說的常見的幾種內存泄漏的狀況。

內存膨脹 即在短期內內存佔用極速上升到達一個峯值,想要避免須要使用技術手段減小對內存的佔用。

頻繁 GC 就是 GC(垃圾回收) 執行的特別頻繁,通常出如今頻繁使用大的臨時變量致使新生代空間被裝滿的速度極快,而每次新生代裝滿時就會觸發 GC,頻繁 GC 一樣會致使頁面卡頓,想要避免的話就不要搞太多的臨時變量,由於臨時變量不用了就會被回收,這和咱們內存泄漏中說避免使用全局變量衝突,其實,只要把握好其中的度,不太過度就 沒事。

相關文章
相關標籤/搜索