1. 程式人生 > >Chrome 開發工具之 Memory

Chrome 開發工具之 Memory

開發過程中難免會遇到記憶體問題,emmm... 本文主要記錄一下Chrome排查記憶體問題的面板,官網也有,但有些說明和例子跟不上新的版本了,也不夠詳細...   !!! 多圖預警!!!    簡單的記憶體資訊列表   如果只想檢視當前瀏覽器的各個 tab 正在使用的記憶體量,則在 Setting - More Tools - Task Manager 即可。效果如下圖:     那個列表裡的可勾選項,沒看錯,是對於可選的資訊資料列。   那個 End Process 按鈕,沒看錯,選擇一項後,可以在瀏覽器所起的任務列表裡關閉改任務(任務可以是開啟頁面的 tab、Chrome 自身一些專案及擴充套件外掛...為什麼不是按鈕上寫的 process,因為這些任務裡面只有部分是在機器程序列表裡列出存在的,強行嚴格 ~ToT~ )    需要看記憶體的實時變化過程,可以在 Chrome - Performance 面板檢視時間軸上記憶體變化情況,其中會有 `js Heap` 記錄的選項,詳情參考 Chrome 開發工具之 Timeline/Performance   Memory 面板初始   如果想要看更多的記憶體資訊快照,則需要開啟 Chrome 瀏覽器的開發者工具中的 Memory 面板了,下面就簡單介紹一下該面板的使用。 它大概是長這樣:  

上面有三個按鈕:

  • Heap snapshot - 用以列印堆快照,堆快照檔案顯示頁面的 javascript 物件和相關 DOM 節點之間的記憶體分配
  • Allocation instrumentation on timeline - 在時間軸上記錄記憶體資訊,隨著時間變化記錄記憶體資訊。
  • Allocation sampling - 記憶體資訊取樣,使用取樣的方法記錄記憶體分配。此配置檔案型別具有最小的效能開銷,可用於長時間執行的操作。它提供了由 javascript 執行堆疊細分的良好近似值分配。
  各自舉些例子吧,方便理解   Heap snapshot   給個 html,裡面只有一句 js 程式碼 var _____testArray_____ = [ {value: 'hello'} ]; ,打個堆疊看看:     右上那塊區域,從左到右有三個操作:檢視方式、物件歸類的篩選、物件選擇。   左邊有 `Summary` 字樣的那個,可以選擇檢視記憶體快照的方式,可選方式如下:
  • Summary - 可以顯示按建構函式名稱分組的物件。使用此檢視可以根據按建構函式名稱分組的型別深入瞭解物件(及其記憶體使用),適用於跟蹤 DOM 洩漏。
  • Comparison - 可以顯示兩個快照之間的不同。使用此檢視可以比較兩個(或多個)記憶體快照在某個操作前後的差異。檢查已釋放記憶體的變化和參考計數,可以確認是否存在記憶體洩漏及其原因。
  • Containment - 此檢視提供了一種物件結構檢視來分析記憶體使用,由頂級物件作為入口。
  • Statistic - 記憶體使用餅狀的統計圖。
附上 Comparison 效果,大致如下: 程式碼:
var _____testArray_____ = [{ value: 'hello' }]

function someTodo() {
  _____testArray_____.push({
    value: ':::::::::'
  })
}

document.querySelector('#btn').addEventListener('click', someTodo, false)
  點選按鈕後,陣列中 push 了新的一項物件 圖(array 那塊列表展開就看不到下面列表了,就沒展開):   附上 Containment 檢視,它的排列稍微有些不同,大致如下:

 

入口有:
  • DOMWindow - 是被視為 JavaScript 程式碼 "全域性" 物件的物件。
  • GC - VM 的垃圾使用的實際 GC 根。GC 根可以由內建物件對映、符號表、VM 執行緒堆疊、編譯快取、控制代碼作用域和全域性控制代碼組成。
  • 原生物件 - 是 "推送" 至 JavaScript 虛擬機器內以允許自動化的瀏覽器物件,例如 DOM 節點和 CSS 規則。
  中間的 `Class filter` 只能夠按照列出來的 Constructor 值進行篩選。   右邊的 `All objects` 能夠選擇檢視哪些階段的物件、如 "Objects allocated before Snapshot1"、"Objects allocated between Snapshot1 and Snapshot2"
右中那塊區域顯示的記憶體快照資訊,可以在各個資料上右鍵選擇一些操作( `Reveal in Summary view` ),各個欄位代表資訊如下:
  • Contructor - 表示使用此建構函式建立的所有物件
  • Distance - 顯示使用節點最短簡單路徑時距根節點的距離
  • Shallow Size - 顯示通過特定建構函式建立的所有物件淺層大小的總和。淺層大小是指物件自身佔用的記憶體大小(一般來說,陣列和字串的淺層大小比較大)
  • Retained Size - 顯示同一組物件中最大的保留大小。某個物件刪除後(其依賴項不再可到達)可以釋放的記憶體大小稱為保留大小。
  • #New - Comparison 特有 - 新增項
  • #Deleted - Comparison 特有 - 刪除項
  • #Delta - Comparison 特有 - 增量
  • Alloc. Size - Comparison 特有 - 記憶體分配大小
  • Freed Size - Comparison 特有 - 釋放大小
  • Size Delta - Comparison 特有 - 記憶體增量

右下那塊區域顯示的是被選中物件的詳細資訊,如上面圖片的內容一樣一樣的...可以在各個資料上右鍵選擇一些操作( `Reveal in Summary view` )。
注意:圖中最最下面那塊最有用,就是搜尋,ctrl/command + f 喚出 ~
最後,根據上面的圖來分析一下上面程式碼產生的效果,根據 js 的型別和引用的關係來分析,變數 _____testArray_____ 在列表中的情況是:
  • 基礎型別 string 值為 hello ,記憶體標記是 string@353953,這個 string 值存在於 Object @362113 物件上的 value 屬性上;
  • Object @362113 在 Object 列表裡,在 Array @356493 的索引 0 位置存在該物件的引用;
  • Array @356493 在 Window / @353829 物件上存在引用,屬性名為"___testArray___";
  • Window / @353829 是個 Windows 物件,在 Windows 列表裡。 
"hello" -> 在(string)列表裡 -> string@353953 -> value in Object @362113

    Object  -> 在 Object 列表裡 -> [0] in Array @356493

        Array -> 在(array)列表裡 -> _____testArray_____ in Window / @353829

                Windows ->  在 Windows 列表裡 -> Window / @353829
  Allocation instrumentation on timeline   看完靜態的快照,再來看看動態的。 程式碼如下:
var _____testArray_____ = [{ value: 'hello' }]
var count = 1

function someTodo() {
  // 每次點選 字串長度都以上一次為基礎增加到5倍,拉大差異突出效果,並且之後在字串頭部加上count值做區分
  count *= 5
  var str = new Array(count * 10).join(':')
  _____testArray_____.push({
    value: count + str
  })
}

document.querySelector('#btn').addEventListener('click', someTodo, false)
  選擇 Allocation instrumentation on timeline 點選開始記錄的按鈕,然後得到如圖所示:  

 

每條線的高度與最近分配的物件大小對應,豎線的顏色表示這些物件是否仍然顯示在最終的堆快照中。藍色豎線表示在時間線最後物件仍然顯示,灰色豎線表示物件已在時間線期間分配,但曾對其進行過垃圾回收。(這圖中不是很明顯,放大 devtool 面板後,圖中的藍色線頂部是有部分是灰色的...)   可以選擇時間範圍,檢視該時間範圍內的記憶體變化情況,如上圖 5 次變化的情況分別是:
# 前面的數字代表本次記錄索引,點選了5次

# 0  Shallow Size : 112
Constructor         Distance   Shallow Size    Retained Size
- - - - - - - - - - - - - - - - - - - - - - - - -  - - - - - - - - - - -
(array)×9                3      5008  0%         5008  0%
(system)×60              3      2416  0%         2640  0%
(closure)×1              3      4768  0%         2928  0%
Object×3                 3      144   0%         768   0%
MouseEvent×3             4      112   0%         7200  0%
(string)×2               5      96    0%         96    0%
(concatenated string)×2  4      64    0%         160   0%
Event                    5      56    0%         2040  0%
UIEvent                  5      32    0%         648   0%

# 1
(string)×2               5      296   0%         296   0%
(concatenated string)×2  4      64    0%         360   0%
Object                   3      32    0%         392   0%

# 2
(string)×2               5      1296  0%         1296  0%
(concatenated string)×2  4      64    0%         1360  0%
Object                   3      32    0%         1392  0%

# 3
(string)×2               5      6296  0%         6296  0%
(concatenated string)×2  4      64    0%         6360  0%
Object                   3      32    0%         6392  0%

# 4
(string)×2               5      31296 0%         31296 0%
(array)                  4      80    0%         80    0%
(concatenated string)×2  4      64    0%         31360 0%
(system)                 4      32    0%         32    0%
Object                   3      32    0%         31392 0%

 

當勾選 Record allocation stacks 框後,還可以在 Allocation stack 面板裡打印出呼叫堆疊。 如上面程式碼的效果:

 

Allocation sampling   這個功能根據名稱和說明,不是很看得懂是什麼... 但是,還是通過一些案例給出了效果圖,如下:

 

根據給出的圖,可以看出這塊的功能應該是:哪些函式影響了記憶體的分配,並且該函式所耗記憶體在記憶體分配中佔比多少。 圖中函式可以直接點選跳轉到函式定義的檔案和位置。
記錄完畢,撒花... &nbs