記錄堆積快照

Meggin Kearney
Meggin Kearney
Sofia Emelianova
Sofia Emelianova

瞭解如何使用「記憶體」 >「設定檔」 >「堆積快照」記錄堆積快照,並查看記憶體流失情形。

堆積分析器會根據網頁的 JavaScript 物件和相關 DOM 節點,顯示記憶體分佈情形。您可以利用這個擴充功能拍攝 JS 堆積快照、分析記憶體圖、比較快照並找出記憶體流失問題。詳情請參閱物件保留樹狀結構

拍攝快照

如要拍攝記憶體快照資料:

  1. 在您要分析的頁面上開啟開發人員工具,然後前往「Memory」面板
  2. 依序選取 radio_button_checked「堆積快照」剖析類型、選取 JavaScript VM 執行個體,然後按一下「拍攝快照」

所選剖析類型和 JavaScript VM 執行個體。

「Memory」面板載入並剖析快照時,會在「HEAP SNAPSHOTS」部分的快照標題下方顯示可連線的 JavaScript 物件總大小。

可連線的物件總大小。

快照只會顯示記憶體圖表中可供全域物件存取的物件。拍攝快照一律從垃圾收集開始。

散佈項目物件的堆積快照。

清除快照

如要移除所有快照,請按一下「封鎖」「清除所有設定檔」:

清除所有設定檔。

查看快照

如要檢查不同用途的快照,請在頂端的下拉式選單中選取其中一項檢視畫面:

查看 內容 目的
摘要 按建構函式名稱分組的物件。 用於依據類型尋找物件及其記憶體用量。有助於追蹤 DOM 外洩情形
比較 兩個快照之間的差異。 可用來比較作業前後兩張 (或更多) 快照。檢查釋放記憶體和參考計數中的差異,確認記憶體是否流失以及造成記憶體流失的原因。
遏制 堆積內容 提供更好的物件結構檢視畫面,並協助分析全域命名空間 (視窗) 中參照的物件,找出物件結構。您可運用這項工具分析封閉情形,以及低階檢視物件。
統計資料 記憶體配置的圓餅圖 查看分配給程式碼、字串、JS 陣列、型別陣列和系統物件的記憶體部分實際大小。

從頂端的下拉式選單中選取「摘要」檢視畫面。

摘要檢視

系統一開始會在「Summary」(摘要) 檢視畫面中開啟堆積快照,其中會列出資料欄中的「建構函式」。您可以展開建構函式,查看其執行個體化的物件。

具有展開建構函式的「Summary」檢視畫面。

如要篩除不相關的建構函式,請在「摘要」檢視畫面頂端的「類別篩選器」中,輸入要檢查的名稱。

建構函式名稱旁邊的數字代表由建構函式建立的物件總數。「摘要」資料檢視也會顯示下列資料欄:

  • 距離 - 使用最短的節點路徑,顯示與根層級之間的距離。
  • 淺層大小會顯示特定建構函式建立的所有物件的淺層大小總和。淺層大小是指物件本身所保存的記憶體大小。一般來說,陣列和字串的淺層大小較大。另請參閱「物件大小」。
  • 「保留大小」會顯示相同物件組合之間的保留大小上限。保留大小是指透過刪除物件並使其無法存取其相依項目後,釋出的記憶體大小。另請參閱「物件大小」。

當您展開建構函式時,「Summary」檢視畫面會顯示其所有例項。每個執行個體都會取得相應資料欄中的淺層大小和保留大小明細。@ 字元後方的數字是物件的專屬 ID。可讓您比較每個物件的堆積快照。

摘要中的特殊項目

除了依建構函式分組之外,摘要檢視畫面還會依據以下方式將物件分組:

  • 內建函式,例如 ArrayObject
  • 您在程式碼中定義的函式。
  • 並非以建構函式為基礎的特殊類別。

建構函式項目。

(array)

這個類別包含各種類似內部陣列的物件,這些物件不會直接對應至 JavaScript 中顯示的物件。

舉例來說,為方便調整大小,JavaScript Array 物件的內容會儲存在名為 (object elements)[] 的次要內部物件中。同樣地,JavaScript 物件中的已命名屬性通常儲存在名為 (object properties)[] 的次要內部物件中,後者也會列在 (array) 類別中。

(compiled code)

這個類別包含 V8 所需的內部資料,以便執行 JavaScript 或 WebAssembly 定義的函式。每個函式都可以以多種方式表示,從小到大,到大,都能快速呈現。

V8 會自動管理這個類別的記憶體用量。如果函式執行多次,V8 會使用較多記憶體來加快該函式的執行速度。如果函式已有一段時間未執行,V8 可能會清除該函式的內部資料。

(concatenated string)

V8 串連兩個字串 (例如 JavaScript + 運算子) 時,可能會選擇在內部以「串連字串」(也稱為「Rope」資料結構) 在內部表示結果。

V8 不會將兩個來源字串的所有字元複製到新字串,而是會分配一個含有名為 firstsecond 的內部欄位的小型物件,該物件指向兩個來源字串。這有助於 V8 節省時間和記憶體。從 JavaScript 程式碼的角度來看,這些只是一般字串,運作方式與任何其他字串一樣。

InternalNode

這個類別代表在 V8 以外配置的物件,例如由 Blink 定義的 C++ 物件。

如要查看 C++ 類別名稱,請使用 Chrome for Testing,然後執行下列操作:

  1. 開啟開發人員工具,然後依序開啟「設定」 >「實驗」 >「check_box」顯示在堆積快照中顯示內部資料的選項
  2. 開啟「記憶體」面板,選取「Radio_button_checked」,然後開啟 radio_button_checked「公開內部資料 (包括其他實作專屬詳細資料)」
  3. 重現導致 InternalNode 保留大量記憶體的問題。
  4. 拍攝堆積快照。在這個快照中,物件使用 C++ 類別名稱,而非 InternalNode
(object shape)

V8 中的 Fast Properties 所述,V8 會追蹤「隱藏類別」 (或「形狀」),以便高效率地以相同順序呈現多個具有相同屬性的物件。這個類別包含這些隱藏類別,名稱為 system / Map (與 JavaScript Map 無關) 以及相關資料。

(sliced string)

V8 需要使用子字串 (例如 JavaScript 程式碼呼叫 String.prototype.substring()) 時,V8 可能會選擇分配切割字串物件,而非複製原始字串中的所有相關字元。這個新物件包含指向原始字串的指標,並說明原始字串中要使用的字元範圍。

從 JavaScript 程式碼的角度來看,這些只是一般字串,運作方式與任何其他字串一樣。如果切片字串保留了大量記憶體,則程式可能會觸發問題 2869,若採取刻意步驟來「扁平」切割字串,或許會有幫助。

system / Context

system / Context 類型的內部物件包含來自「閉包」的本機變數,這是巢狀函式可以存取的 JavaScript 範圍。

每個函式執行個體都包含執行所在 Context 的內部指標,以便存取這些變數。即使 Context 物件不會直接從 JavaScript 中顯示,您還是可以直接控管這些物件。

(system)

這個類別包含各種內部物件,尚未以更有意義的方式分類。

比較資料檢視

「比較」檢視畫面會比較多個快照,協助您找出流失的物件。舉例來說,執行和復原操作 (例如開啟文件和關閉文件) 時,不應留下額外物件。

如何確認特定作業不會造成流失問題:

  1. 在執行作業前拍攝堆積快照。
  2. 執行作業。因此,請以您認為可能導致資訊外洩的方式與網頁互動。
  3. 執行反向作業。也就是採取相反的互動,然後重複多次。
  4. 建立第二個堆積快照,並將檢視畫面變更為「Comparison」(比較),並與 Snapshot 1 進行比較。

「Comparison」(比較) 檢視畫面會顯示兩張快照之間的差異。展開總計項目時,會顯示新增和刪除的物件執行個體:

與快照 1 比較。

遏制檢視

「隔離」檢視畫面是應用程式物件結構的「鳥瞰圖」。可讓您查看函式關閉情形、觀察組成 JavaScript 物件的 VM 內部物件,並瞭解應用程式在極低層級使用多少記憶體。

檢視畫面提供了多個進入點:

  • DOMWindow 物件。JavaScript 程式碼適用的全域物件。
  • GC 根。VM 垃圾收集器使用的 GC 根層級。GC 根層級可包含內建的物件對應、符號表、VM 執行緒堆疊、編譯快取、處理範圍和全域控制代碼。
  • 原生物件。在 JavaScript 虛擬機器中「推送」瀏覽器物件,以便進行自動化作業,例如 DOM 節點和 CSS 規則。

「遏制」檢視畫面。

「消費者」專區

「Memory」(記憶體) 面板底部的「Retainers」(保留器) 部分會顯示指向檢視畫面中所選物件的物件。

「協定」部分。

在這個範例中,選取的字串是由 Item 執行個體的 x 屬性保留。

尋找特定物件

如要在已收集的堆積中尋找物件,請使用 Ctrl + F 鍵搜尋,然後輸入物件 ID。

為函式命名,以便區分道路封閉

為函式命名,以便在快照中區分閉包。

舉例來說,以下程式碼並未使用已命名函式:

function createLargeClosure() {
  var largeStr = new Array(1000000).join('x');

  var lC = function() { // this is NOT a named function
    return largeStr;
  };

  return lC;
}

本範例執行以下動作:

function createLargeClosure() {
  var largeStr = new Array(1000000).join('x');

  var lC = function lC() { // this IS a named function
    return largeStr;
  };

  return lC;
}

封閉中的已命名函式。

找出 DOM 外洩情形

堆積分析器能夠反映瀏覽器原生物件 (DOM 節點和 CSS 規則) 與 JavaScript 物件之間的雙向依附性。這有助於發現其他因忘記卸離的 DOM 子樹而造成的隱形流失問題。

DOM 外洩事件可能比您想像的還大。請參考以下範例。#tree 垃圾收集時間為何?

  var select = document.querySelector;
  var treeRef = select("#tree");
  var leafRef = select("#leaf");
  var body = select("body");

  body.removeChild(treeRef);

  //#tree can't be GC yet due to treeRef
  treeRef = null;

  //#tree can't be GC yet due to indirect
  //reference from leafRef

  leafRef = null;
  //#NOW #tree can be garbage collected

#leaf 會維持對其父項 (parentNode) 的參照,並且以遞迴方式最多為 #tree,因此只有在 leafRef 為空值時,才是 #tree 下的「整個」樹狀結構,以供 GC 使用。

DOM 子樹狀結構