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

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