修正記憶體問題

凱斯巴斯克文
Kayce Basques

瞭解如何使用 Chrome 和開發人員工具找出會影響頁面效能的記憶體問題,包括記憶體流失、記憶體膨脹和頻繁的垃圾收集。

摘要

  • 使用 Chrome 工作管理員瞭解網頁目前的記憶體用量。
  • 透過時間軸記錄以視覺化方式呈現記憶體用量變化。
  • 使用堆積快照找出卸離的 DOM 樹狀結構 (記憶體流失的常見原因)。
  • 透過分配時間軸記錄功能,瞭解在 JS 堆積中配置新記憶體的時機。

總覽

採用 RAIL 效能模型的精神,效能工作的重點應該是使用者。

記憶體問題至關重要,因為使用者通常可以察覺。使用者可以透過下列方式感知記憶體問題:

  • 網頁成效隨著時間逐漸降低。這可能是記憶體流失的症狀。記憶體流失是指因網頁中的錯誤,隨著時間而逐漸地使用更多記憶體。
  • 網頁效能一直不佳。這可能是記憶體激增的症狀。記憶體耗損是指網頁使用的記憶體超過達到最佳頁面速度所需的記憶體容量。
  • 網頁效能延遲,或似乎經常暫停。這可能代表系統頻繁收集垃圾。垃圾收集是指瀏覽器收回記憶體。這時瀏覽器就會決定。在收集期間,所有指令碼都會暫停執行。因此,如果瀏覽器垃圾收集大量資料,指令碼執行作業就會經常暫停。

記憶體大量增加:多麼「太多」?

記憶體流失的定義很容易。如果網站逐步使用更多記憶體,你就會得到流失。但記憶體膨脹比較困難。何謂「使用過多記憶體」?

不同裝置和瀏覽器的功能各不相同,因此這裡不包括硬性號碼。 在高階智慧型手機上順暢運作的網頁,可能會在低階智慧型手機上當機。

重點就在於使用 RAIL 模型並專注於使用者。瞭解使用者最喜歡哪些裝置,然後在這些裝置上測試您的網頁。如果使用體驗持續不佳,頁面可能會超出這些裝置的記憶體容量。

使用 Chrome 工作管理員即時監控記憶體用量

使用 Chrome 工作管理員開始調查記憶體問題。工作管理員是一種即時監控功能,會顯示網頁目前使用的記憶體量。

  1. 按下 Shift+Esc 鍵,或前往 Chrome 主選單,然後選取「更多工具」 >「工作管理員」,開啟工作管理員。

    開啟工作管理員

  2. 在工作管理員的表格標題上按一下滑鼠右鍵,並啟用「JavaScript 記憶體」

    啟用 JS 記憶體

透過這兩欄,您可以瞭解網頁使用記憶體的各種資訊:

  • 「Memory」欄代表原生記憶體。DOM 節點儲存在原生記憶體中。如果這個值正在增加,就表示正在建立 DOM 節點。
  • 「JavaScript 記憶體」欄代表 JS 堆積。此欄包含兩個值。您感興趣的值是即時數字 (括號中的數字)。即時數字代表網頁上可連線物件的記憶體用量。如果這個數字正在增加,表示系統正在建立新物件,或是現有物件正在增加。

透過效能錄製影片以視覺化方式呈現記憶體流失情形

您也可以使用「成效」面板做為調查的起點。「效能」面板會以視覺化方式呈現網頁在一段時間內的記憶體用量。

  1. 開啟開發人員工具中的「效能」面板。
  2. 勾選「Memory」核取方塊。
  3. 錄影

如要展示效能記憶體記錄,請考慮以下程式碼:

var x = [];

function grow() {
  for (var i = 0; i < 10000; i++) {
    document.body.appendChild(document.createElement('div'));
  }
  x.push(new Array(1000000).join('x'));
}

document.getElementById('grow').addEventListener('click', grow);

每次按下程式碼中參照的按鈕時,文件主體都會附加上萬個 div 節點,並將一串一百萬個 x 字元推送至 x 陣列。執行這個程式碼會產生時間軸錄製內容,如以下螢幕截圖所示:

簡易成長範例

首先,說明使用者介面。「Overview」窗格 (在「NET」下方) 中的「HEAP」圖表代表 JS 堆積。「Overview」(總覽) 窗格下方的「Counter」(計數器) 窗格。您可以在這裡查看依 JS 堆積 (與「總覽」窗格中的「HEAP」圖表相同)、文件、DOM 節點、事件監聽器和 GPU 記憶體細分的記憶體用量。停用核取方塊後,圖表就會隱藏該核取方塊。

現在,我們將程式碼分析與螢幕截圖進行比較。查看節點計數器 (綠色圖表) 時,可以發現這個計數器與程式碼完全吻合。節點數量會以不同的步驟增加。您可以假設節點數增加時都是對 grow() 的呼叫。JS 堆積圖 (藍色圖表) 並不簡單。為遵循最佳做法,第一個 Dip 實際上是強制的垃圾收集 (按下「collect garbage」按鈕可觸發)。在記錄進度期間,您會看到 JS 堆積大小遽增。這是正常現象:JavaScript 程式碼會在每次按鈕點擊時建立 DOM 節點,且會在建立 100 萬個字元的字串時執行大量作業。重點在於 JS 堆積的結束位置高於開始 (這裡的「開始」是指強制垃圾收集之後的點)。在現實生活中,如果您看到這種增加 JS 堆積大小或節點大小的模式,可能表示記憶體流失。

透過堆積快照找出卸離的 DOM 樹狀結構記憶體流失情形

只有當網頁的 DOM 樹狀結構或 JavaScript 程式碼沒有參照該 DOM 節點時,才能對 DOM 節點進行垃圾收集。如果節點從 DOM 樹狀結構中移除,但有部分 JavaScript 仍參照該節點,則節點會被視為「卸離」。卸離 DOM 節點是記憶體流失的常見原因。本節將說明如何使用開發人員工具的堆積分析器識別卸離的節點。

以下是卸離 DOM 節點的簡易範例。

var detachedTree;

function create() {
  var ul = document.createElement('ul');
  for (var i = 0; i < 10; i++) {
    var li = document.createElement('li');
    ul.appendChild(li);
  }
  detachedTree = ul;
}

document.getElementById('create').addEventListener('click', create);

按一下程式碼參照的按鈕,會建立含有 10 個 li 子項的 ul 節點。這些節點是由程式碼參照,但不存在於 DOM 樹狀結構中,因此會卸離。

堆積快照是識別卸離節點的一種方式。顧名思義,堆積快照會如何在快照的時間點,在網頁的 JS 物件和 DOM 節點之間分配記憶體。

如要建立快照,請開啟開發人員工具並前往「Memory」面板,選取「Heap Snapshot」圓形按鈕,然後按下「Take Snapshot」按鈕。

拍攝堆積快照

快照可能需要一些時間才能處理及載入。完成後,從左側面板中選取 (名為 HEAP SNAPSHOTS)。

在「類別篩選器」文字方塊中輸入 Detached,搜尋卸離的 DOM 樹狀結構。

篩選卸離節點

展開 Carats 即可調查卸除的樹。

調查卸除的樹

以黃色標明的節點有直接參照 JavaScript 程式碼中的這些節點。以紅色醒目顯示的節點沒有直接參照。它們是黃色節點樹狀結構的一部分,因此仍然有效。一般來說,您會想要注意黃色節點。修正程式碼,避免黃色節點的運作時間超過這個時間。此外,您也可以移除黃色節點樹狀結構中的紅色節點。

按一下黃色節點即可進一步調查。在「Objects」(物件) 窗格中,你可以進一步瞭解參照該物件的程式碼。例如,在下方螢幕截圖中,您可以看到 detachedTree 變數正在參照節點。如要修正這個特定記憶體流失問題,您必須研究使用 detachedTree 的程式碼,並確保不再需要的節點參照。

調查黃色節點

使用分配時間軸找出 JS 堆積記憶體流失

「配置時間軸」是另一項工具,可協助您追蹤 JS 堆積中的記憶體流失。

如要示範配置時間軸,請考慮使用下列程式碼:

var x = [];

function grow() {
  x.push(new Array(1000000).join('x'));
}

document.getElementById('grow').addEventListener('click', grow);

每次推送程式碼中參照的按鈕時,系統會在 x 陣列中加入一百萬個字元的字串。

如要錄製分配時間軸,請開啟開發人員工具,前往「Profiles」面板,選取「Record Allocation Timeline」圓形按鈕,按下「Start」按鈕,接著執行疑似導致記憶體流失的動作,然後在完成後按下「Stop recording」按鈕 (停止錄影按鈕)。

錄製時,請留意「配置時間軸」中是否有任何藍色長條,如下方螢幕截圖所示。

新的分配金額

這些藍色長條代表新的記憶體配置。這些新的記憶體配置可用來避免記憶體流失。您可以縮放長條,篩選「建構函式」窗格,只顯示指定時間範圍內分配的物件。

縮放的分配時間軸

展開物件並按一下其值,即可在「Object」窗格中查看物件的詳細資料。舉例來說,在下方螢幕截圖中,您可以查看新分配物件的詳細資料,即可看到該物件已分配至 Window 範圍中的 x 變數。

物件詳細資料

依函式調查記憶體配置

在「Memory」面板中,使用「Allocation Sampling」類型查看 JavaScript 函式的記憶體配置

記錄分配分析器

  1. 選取「配置取樣」圓形按鈕。如果頁面上有工作站,您可以使用「Start」按鈕旁的下拉式選單,選取該工作站做為剖析目標。
  2. 按下「Start」按鈕。
  3. 在想調查的網頁上執行動作。
  4. 完成所有動作後,請按下「停止」按鈕。

開發人員工具會根據函式顯示記憶體配置的細目。預設檢視畫面為「Heavy (Bottom Up)」,會在頂端顯示分配最多記憶體的函式。

分配設定檔

找出垃圾收集頻率

如果網頁似乎經常暫停,表示可能有垃圾收集問題。

您可以使用 Chrome 工作管理員或時間軸記憶體記錄,找出常見的垃圾收集行為。在工作管理員中,經常上升及下降的「記憶體」或「JavaScript 記憶體」值代表常見的垃圾收集。在時間軸記錄中,如果 JavaScript 堆積或節點數量經常增加,則代表系統經常進行垃圾收集。

找出問題後,您就能利用配置時間軸記錄功能,找出記憶體的配置位置,以及哪些函式導致配置作業。