Chrome 開發工具之 Memory

  • 2019 年 10 月 3 日
  • 筆記

開發過程中難免會遇到記憶體問題,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
 
這個功能根據名稱和說明,不是很看得懂是什麼... 但是,還是通過一些案例給出了效果圖,如下:

 

根據給出的圖,可以看出這塊的功能應該是:哪些函數影響了記憶體的分配,並且該函數所耗記憶體在記憶體分配中佔比多少。
圖中函數可以直接點擊跳轉到函數定義的文件和位置。

記錄完畢,撒花...
 

Tags: