Tìm hiểu cách ghi lại ảnh chụp nhanh vùng nhớ khối xếp bằng Memory (Bộ nhớ) > Profiles (Hồ sơ) > Heap snapshot (Ảnh chụp nhanh vùng nhớ khối xếp) và tìm sự cố rò rỉ bộ nhớ.
Trình phân tích vùng nhớ khối xếp cho thấy mức phân phối bộ nhớ theo các đối tượng JavaScript của trang và các nút DOM có liên quan. Sử dụng công cụ này để chụp ảnh nhanh vùng nhớ khối xếp JS, phân tích biểu đồ bộ nhớ, so sánh ảnh chụp nhanh và tìm lỗi rò rỉ bộ nhớ. Để biết thêm thông tin, hãy xem phần Cây giữ lại đối tượng.
Chụp nhanh
Cách chụp ảnh nhanh vùng nhớ khối xếp:
- Trên trang mà bạn muốn phân tích, hãy mở Công cụ cho nhà phát triển rồi chuyển đến bảng điều khiển Bộ nhớ.
- Chọn loại hồ sơ Bản tổng quan nhanh về vùng nhớ khối xếp, sau đó chọn một thực thể máy ảo JavaScript rồi nhấp vào Lấy bản tổng quan nhanh.
Khi bảng điều khiển Memory (Bộ nhớ) tải và phân tích cú pháp ảnh chụp nhanh, bảng điều khiển này sẽ hiển thị tổng kích thước của các đối tượng JavaScript có thể truy cập được bên dưới tiêu đề ảnh chụp nhanh trong phần HEAP SNAPSHOTS (ẢNH CHỤP NHANH VÙNG NHỚ KÉP).
Bản lưu toàn cảnh chỉ hiển thị các đối tượng từ biểu đồ bộ nhớ mà đối tượng toàn cục có thể truy cập. Việc chụp nhanh luôn bắt đầu bằng việc thu thập rác.
Xoá ảnh chụp nhanh
Để xoá tất cả ảnh chụp nhanh, hãy nhấp vào
Xoá mọi hồ sơ:Xem ảnh chụp nhanh
Để kiểm tra ảnh chụp nhanh từ nhiều góc độ cho nhiều mục đích, hãy chọn một trong các chế độ xem trong trình đơn thả xuống ở trên cùng:
Xem | Nội dung | Mục đích |
---|---|---|
Tóm tắt | Các đối tượng được nhóm theo tên hàm khởi tạo. | Sử dụng công cụ này để tìm đối tượng và mức sử dụng bộ nhớ của đối tượng dựa trên loại. Hữu ích cho việc theo dõi rò rỉ DOM. |
So sánh | Sự khác biệt giữa hai ảnh chụp nhanh. | Sử dụng hàm này để so sánh hai (hoặc nhiều) ảnh chụp nhanh, trước và sau khi thực hiện một thao tác. Xác nhận sự hiện diện và nguyên nhân gây ra sự cố rò rỉ bộ nhớ bằng cách kiểm tra delta trong bộ nhớ được giải phóng và số lượng tham chiếu. |
Phương thức chứa | Nội dung vùng nhớ khối xếp | Cung cấp chế độ xem tốt hơn về cấu trúc đối tượng và giúp phân tích các đối tượng được tham chiếu trong không gian tên toàn cục (cửa sổ) để tìm những đối tượng giữ lại các đối tượng đó. Sử dụng công cụ này để phân tích các hàm đóng và tìm hiểu các đối tượng ở cấp thấp. |
Số liệu thống kê | Biểu đồ hình tròn về mức phân bổ bộ nhớ | Xem kích thước tương đối của các phần bộ nhớ được phân bổ cho mã, chuỗi, mảng JS, mảng đã nhập và đối tượng hệ thống. |
Chế độ xem tóm tắt
Ban đầu, một ảnh chụp nhanh vùng nhớ khối xếp sẽ mở trong chế độ xem Summary (Tóm tắt) liệt kê Constructor (Hàm khởi tạo) trong một cột. Bạn có thể mở rộng hàm khởi tạo để xem các đối tượng mà hàm khởi tạo tạo bản sao.
Để lọc ra các hàm khởi tạo không liên quan, hãy nhập tên mà bạn muốn kiểm tra trong Bộ lọc lớp ở đầu chế độ xem Tóm tắt.
Số bên cạnh tên hàm khởi tạo cho biết tổng số đối tượng được tạo bằng hàm khởi tạo. Chế độ xem Tóm tắt cũng hiển thị các cột sau:
- Khoảng cách cho biết khoảng cách đến gốc bằng cách sử dụng đường dẫn đơn giản ngắn nhất của các nút.
- Kích thước nông cho biết tổng kích thước nông của tất cả đối tượng do một hàm khởi tạo nhất định tạo ra. Kích thước nông là kích thước bộ nhớ mà chính đối tượng giữ. Nhìn chung, các mảng và chuỗi có kích thước nông lớn hơn. Xem thêm phần Kích thước đối tượng.
- Kích thước được giữ lại cho biết kích thước được giữ lại tối đa trong cùng một nhóm đối tượng. Kích thước được giữ lại là kích thước bộ nhớ mà bạn có thể giải phóng bằng cách xoá một đối tượng và khiến các phần phụ thuộc của đối tượng đó không thể truy cập được nữa. Xem thêm phần Kích thước đối tượng.
Khi bạn mở rộng một hàm khởi tạo, chế độ xem Summary (Tóm tắt) sẽ hiển thị tất cả các thực thể của hàm khởi tạo đó. Mỗi thực thể sẽ nhận được bảng chi tiết về kích thước nông và kích thước được giữ lại trong các cột tương ứng. Số sau ký tự @
là mã nhận dạng duy nhất của đối tượng. Tính năng này cho phép bạn so sánh ảnh chụp nhanh vùng nhớ khối xếp trên cơ sở mỗi đối tượng.
Bộ lọc hàm khởi tạo
Chế độ xem Tóm tắt cho phép bạn lọc hàm khởi tạo dựa trên các trường hợp phổ biến về việc sử dụng bộ nhớ không hiệu quả.
Để sử dụng các bộ lọc này, hãy chọn một trong các tuỳ chọn sau trong trình đơn thả xuống ở ngoài cùng bên phải trong thanh thao tác:
- Tất cả đối tượng: tất cả đối tượng được chụp bằng ảnh chụp nhanh hiện tại. Được đặt theo mặc định.
- Các đối tượng được phân bổ trước ảnh chụp nhanh 1: các đối tượng được tạo và vẫn còn trong bộ nhớ trước khi chụp ảnh chụp nhanh đầu tiên.
- Các đối tượng được phân bổ giữa Ảnh chụp nhanh 1 và Ảnh chụp nhanh 2: xem sự khác biệt về đối tượng giữa ảnh chụp nhanh gần đây nhất và ảnh chụp nhanh trước đó. Mỗi ảnh chụp nhanh mới sẽ thêm một giá trị gia tăng của bộ lọc này vào danh sách thả xuống.
- Chuỗi trùng lặp: giá trị chuỗi đã được lưu trữ nhiều lần trong bộ nhớ.
- Các đối tượng được các nút đã tách giữ lại: các đối tượng được duy trì hoạt động vì một nút DOM đã tách tham chiếu đến các đối tượng đó.
- Các đối tượng được bảng điều khiển DevTools giữ lại: các đối tượng được lưu giữ trong bộ nhớ vì chúng được đánh giá hoặc tương tác thông qua bảng điều khiển DevTools.
Mục nhập đặc biệt trong phần Tóm tắt
Ngoài việc nhóm theo hàm khởi tạo, chế độ xem Tóm tắt cũng nhóm các đối tượng theo:
- Các hàm tích hợp sẵn như
Array
hoặcObject
. - Các phần tử HTML được nhóm theo thẻ, ví dụ:
<div>
,<a>
,<img>
và các phần tử khác. - Các hàm bạn đã xác định trong mã.
- Các danh mục đặc biệt không dựa trên hàm khởi tạo.
(array)
Danh mục này bao gồm nhiều đối tượng giống mảng nội bộ không trực tiếp tương ứng với các đối tượng hiển thị trong JavaScript.
Ví dụ: nội dung của các đối tượng Array
JavaScript được lưu trữ trong một đối tượng nội bộ phụ có tên là (object elements)[]
, để cho phép đổi kích thước dễ dàng hơn. Tương tự, các thuộc tính được đặt tên trong đối tượng JavaScript thường được lưu trữ trong các đối tượng nội bộ phụ có tên là (object properties)[]
. Các đối tượng này cũng được liệt kê trong danh mục (array)
.
(compiled code)
Danh mục này bao gồm dữ liệu nội bộ mà V8 cần để có thể chạy các hàm do JavaScript hoặc WebAssembly xác định. Mỗi hàm có thể được biểu thị theo nhiều cách, từ nhỏ và chậm đến lớn và nhanh.
V8 tự động quản lý mức sử dụng bộ nhớ trong danh mục này. Nếu một hàm chạy nhiều lần, V8 sẽ sử dụng nhiều bộ nhớ hơn cho hàm đó để hàm có thể chạy nhanh hơn. Nếu một hàm không chạy trong một thời gian, V8 có thể xoá dữ liệu nội bộ cho hàm đó.
(concatenated string)
Khi nối hai chuỗi, chẳng hạn như với toán tử +
của JavaScript, V8 có thể chọn biểu thị kết quả nội bộ dưới dạng "chuỗi nối" còn gọi là cấu trúc dữ liệu Rope.
Thay vì sao chép tất cả ký tự của hai chuỗi nguồn vào một chuỗi mới, V8 sẽ phân bổ một đối tượng nhỏ có các trường nội bộ có tên là first
và second
, trỏ đến hai chuỗi nguồn. Điều này cho phép V8 tiết kiệm thời gian và bộ nhớ. Từ quan điểm của mã JavaScript, đây chỉ là các chuỗi thông thường và hoạt động giống như mọi chuỗi khác.
InternalNode
Danh mục này đại diện cho các đối tượng được phân bổ bên ngoài V8, chẳng hạn như các đối tượng C++ do Blink xác định.
Để xem tên lớp C++, hãy sử dụng Chrome for Testing (Chrome cho kiểm thử) và làm như sau:
- Mở DevTools rồi bật Settings (Cài đặt) > Experiments (Thử nghiệm) > Show option to expose internals in heap snapshots (Hiện tuỳ chọn để hiển thị nội dung bên trong trong ảnh chụp nhanh vùng nhớ khối xếp).
- Mở bảng điều khiển Memory (Bộ nhớ), chọn Heap snapshot (Bản tổng quan nhanh về vùng nhớ khối xếp) rồi bật Expose internals (includes additional implementation-specific details) (Hiện dữ liệu nội bộ (bao gồm thông tin bổ sung liên quan đến việc triển khai)).
- Tái hiện vấn đề khiến
InternalNode
giữ lại nhiều bộ nhớ. - Chụp ảnh chụp nhanh của vùng nhớ khối xếp. Trong ảnh chụp nhanh này, các đối tượng có tên lớp C++ thay vì
InternalNode
.
(object shape)
Như mô tả trong phần Thuộc tính nhanh trong V8, V8 theo dõi các lớp ẩn (hoặc hình dạng) để có thể trình bày hiệu quả nhiều đối tượng có cùng thuộc tính theo cùng một thứ tự. Danh mục này chứa các lớp ẩn đó, được gọi là system / Map
(không liên quan đến JavaScript Map
) và dữ liệu liên quan.
(sliced string)
Khi V8 cần lấy một chuỗi con, chẳng hạn như khi mã JavaScript gọi String.prototype.substring()
, V8 có thể chọn phân bổ đối tượng chuỗi được cắt thay vì sao chép tất cả các ký tự có liên quan từ chuỗi gốc. Đối tượng mới này chứa con trỏ đến chuỗi gốc và mô tả phạm vi ký tự trong chuỗi gốc cần sử dụng.
Từ quan điểm của mã JavaScript, đây chỉ là các chuỗi thông thường và hoạt động giống như mọi chuỗi khác. Nếu một chuỗi đã cắt đang giữ lại nhiều bộ nhớ, thì chương trình có thể đã kích hoạt Vấn đề 2869 và có thể sẽ có lợi nếu bạn thực hiện các bước có chủ ý để "làm phẳng" chuỗi đã cắt.
system / Context
Các đối tượng nội bộ thuộc loại system / Context
chứa các biến cục bộ từ một hàm đóng – một phạm vi JavaScript mà hàm lồng nhau có thể truy cập.
Mỗi thực thể hàm chứa một con trỏ nội bộ đến Context
mà hàm đó thực thi để có thể truy cập vào các biến đó. Mặc dù các đối tượng Context
không hiển thị trực tiếp từ JavaScript, nhưng bạn có quyền kiểm soát trực tiếp đối với các đối tượng đó.
(system)
Danh mục này chứa nhiều đối tượng nội bộ (chưa) được phân loại theo cách có ý nghĩa hơn.
Chế độ xem so sánh
Chế độ xem So sánh cho phép bạn tìm các đối tượng bị rò rỉ bằng cách so sánh nhiều ảnh chụp nhanh với nhau. Ví dụ: khi thực hiện một thao tác và đảo ngược thao tác đó, chẳng hạn như mở và đóng một tài liệu, bạn không được để lại các đối tượng thừa.
Cách xác minh rằng một thao tác nhất định không tạo ra rò rỉ:
- Chụp ảnh chụp nhanh vùng nhớ khối xếp trước khi thực hiện một thao tác.
- Thực hiện một thao tác. Tức là tương tác với một trang theo cách mà bạn cho rằng có thể gây ra rò rỉ.
- Thực hiện thao tác đảo ngược. Tức là thực hiện tương tác ngược lại và lặp lại vài lần.
- Chụp ảnh chụp nhanh vùng nhớ khối xếp thứ hai và thay đổi chế độ xem thành So sánh, so sánh ảnh chụp nhanh này với Ảnh chụp nhanh 1.
Chế độ xem So sánh cho thấy sự khác biệt giữa hai ảnh chụp nhanh. Khi mở rộng mục nhập tổng, các thực thể đối tượng đã thêm và đã xoá sẽ xuất hiện:
Khung hiển thị vùng chứa
Chế độ xem Containment (Phạm vi chứa) là "tổng quan từ trên cao" về cấu trúc đối tượng của ứng dụng. Công cụ này cho phép bạn xem bên trong các hàm đóng, quan sát các đối tượng nội bộ của máy ảo cùng tạo nên các đối tượng JavaScript và hiểu được mức sử dụng bộ nhớ của ứng dụng ở cấp rất thấp.
Thành phần hiển thị này cung cấp một số điểm truy cập:
- Đối tượng DOMWindow. Đối tượng toàn cục cho mã JavaScript.
- GC gốc. GC gốc do bộ thu gom rác của máy ảo sử dụng. Các thư mục gốc của GC có thể bao gồm bản đồ đối tượng tích hợp, bảng ký hiệu, ngăn xếp luồng máy ảo, bộ nhớ đệm biên dịch, phạm vi xử lý và tay cầm toàn cục.
- Đối tượng gốc. Các đối tượng trình duyệt được "đẩy" vào bên trong máy ảo JavaScript để cho phép tự động hoá, ví dụ: các nút DOM và quy tắc CSS.
Mục Đường dẫn giữ lại
Phần Retainers (Giữ lại) ở cuối bảng điều khiển Memory (Bộ nhớ) cho thấy các đối tượng trỏ đến đối tượng đã chọn trong thành phần hiển thị. Bảng điều khiển Memory (Bộ nhớ) sẽ cập nhật mục Retainers (Giữ lại) khi bạn chọn một đối tượng khác trong bất kỳ chế độ xem nào ngoại trừ Statistics (Thống kê).
Trong ví dụ này, thuộc tính x
của thực thể Item
sẽ giữ lại chuỗi đã chọn.
Bỏ qua trình lưu giữ
Bạn có thể ẩn các đối tượng giữ lại để tìm hiểu xem có đối tượng nào khác giữ lại đối tượng đã chọn hay không. Với tuỳ chọn này, trước tiên, bạn không cần xoá phần giữ lại này khỏi mã rồi chụp lại ảnh chụp nhanh vùng nhớ khối xếp.
Để ẩn một phần giữ lại, hãy nhấp chuột phải rồi chọn Bỏ qua phần giữ lại này. Các phần tử giữ lại bị bỏ qua được đánh dấu là ignored
trong cột Khoảng cách. Để ngừng bỏ qua tất cả các phần giữ lại, hãy nhấp vào Khôi phục phần giữ lại bị bỏ qua trong thanh thao tác ở trên cùng.
Tìm một đối tượng cụ thể
Để tìm một đối tượng trong vùng nhớ khối xếp đã thu thập, bạn có thể tìm kiếm bằng cách sử dụng tổ hợp phím Ctrl + F rồi nhập mã nhận dạng đối tượng.
Đặt tên hàm để phân biệt các hàm đóng
Việc đặt tên cho các hàm sẽ giúp bạn phân biệt được các hàm đóng trong ảnh chụp nhanh.
Ví dụ: mã sau đây không sử dụng hàm được đặt tên:
function createLargeClosure() {
var largeStr = new Array(1000000).join('x');
var lC = function() { // this is NOT a named function
return largeStr;
};
return lC;
}
Trong khi ví dụ này lại có:
function createLargeClosure() {
var largeStr = new Array(1000000).join('x');
var lC = function lC() { // this IS a named function
return largeStr;
};
return lC;
}
Phát hiện rò rỉ DOM
Trình phân tích tài nguyên vùng nhớ khối xếp có thể phản ánh các phần phụ thuộc hai chiều giữa các đối tượng gốc của trình duyệt (nút DOM và quy tắc CSS) và các đối tượng JavaScript. Điều này giúp phát hiện các sự cố rò rỉ không nhìn thấy được xảy ra do các cây con DOM bị tách rời bị quên trôi nổi xung quanh.
Tình trạng rò rỉ DOM có thể nghiêm trọng hơn bạn nghĩ. Hãy xem ví dụ sau đây. Khi nào rác #tree
được thu gom?
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
duy trì một tệp tham chiếu đến thành phần mẹ (parentNode
) và đệ quy lên đến #tree
, vì vậy, chỉ khi leafRef
bị vô hiệu hoá thì toàn bộ cây trong #tree
mới là ứng cử viên cho GC.