Phần này mô tả các thuật ngữ thường dùng trong phân tích bộ nhớ và áp dụng cho nhiều công cụ phân tích bộ nhớ cho nhiều ngôn ngữ.
Các thuật ngữ và khái niệm được mô tả ở đây đề cập đến Trình phân tích bộ nhớ khối xếp trong Công cụ của Chrome cho nhà phát triển. Nếu bạn từng làm việc với trình phân tích bộ nhớ Java, .NET hoặc một số trình phân tích bộ nhớ khác, thì đây có thể là một bài ôn tập.
Kích thước đối tượng
Hãy coi bộ nhớ là một biểu đồ có các loại dữ liệu gốc (như số và chuỗi) và đối tượng (mảng liên kết). Bạn có thể biểu thị trực quan bằng một biểu đồ có một số điểm kết nối với nhau như sau:
Một đối tượng có thể lưu trữ bộ nhớ theo hai cách:
- Trực tiếp bởi chính đối tượng.
- Ngầm ẩn bằng cách giữ lại các tệp đối chiếu đến các đối tượng khác, do đó ngăn các đối tượng đó bị trình thu gom rác (viết tắt là GC) tự động xử lý.
Khi làm việc với Trình phân tích vùng nhớ khối xếp trong DevTools (một công cụ để điều tra các vấn đề về bộ nhớ trong bảng điều khiển Memory (Bộ nhớ)), bạn có thể thấy mình đang xem một vài cột thông tin khác nhau. Hai thuộc tính đáng chú ý là Kích thước nông và Kích thước giữ lại, nhưng những thuộc tính này đại diện cho điều gì?
Kích thước nông
Đây là kích thước bộ nhớ mà chính đối tượng giữ.
Các đối tượng JavaScript thông thường có một số bộ nhớ được dành riêng cho nội dung mô tả và để lưu trữ các giá trị ngay lập tức. Thông thường, chỉ các mảng và chuỗi mới có thể có kích thước nông đáng kể. Tuy nhiên, các chuỗi và mảng bên ngoài thường có bộ nhớ chính trong bộ nhớ trình kết xuất, chỉ hiển thị một đối tượng trình bao bọc nhỏ trên vùng nhớ khối xếp JavaScript.
Bộ nhớ trình kết xuất là tất cả bộ nhớ của quy trình kết xuất trang được kiểm tra: bộ nhớ gốc + bộ nhớ khối xếp JS của trang + bộ nhớ khối xếp JS của tất cả worker chuyên dụng do trang khởi động. Tuy nhiên, ngay cả một đối tượng nhỏ cũng có thể lưu trữ một lượng lớn bộ nhớ một cách gián tiếp, bằng cách ngăn các đối tượng khác bị xử lý bằng quy trình thu thập rác tự động.
Kích thước được giữ nguyên
Đây là dung lượng bộ nhớ được giải phóng sau khi đối tượng đó bị xoá cùng với các đối tượng phụ thuộc không thể truy cập được từ gốc GC.
GC gốc bao gồm các handle được tạo (trên máy hoặc trên toàn cục) khi tạo tham chiếu từ mã gốc đến đối tượng JavaScript bên ngoài V8. Bạn có thể tìm thấy tất cả các handle như vậy trong ảnh chụp nhanh vùng nhớ khối xếp trong phần GC roots (Gốc GC) > Handle scope (Phạm vi handle) và GC roots (Gốc GC) > Global handles (Handle toàn cục). Việc mô tả các tay điều khiển trong tài liệu này mà không đi sâu vào chi tiết về cách triển khai trình duyệt có thể gây nhầm lẫn. Bạn không cần phải lo lắng về cả thư mục gốc GC và các tay cầm.
Có rất nhiều gốc GC nội bộ, hầu hết đều không thú vị đối với người dùng. Từ quan điểm của ứng dụng, có các loại gốc sau:
- Đối tượng toàn cục của cửa sổ (trong mỗi iframe). Có một trường khoảng cách trong ảnh chụp nhanh vùng nhớ khối xếp là số lượng tệp tham chiếu thuộc tính trên đường dẫn giữ lại ngắn nhất từ cửa sổ.
- Cây DOM tài liệu bao gồm tất cả các nút DOM gốc có thể truy cập được bằng cách duyệt qua tài liệu. Không phải tất cả các phần tử đều có trình bao bọc JS, nhưng nếu có thì trình bao bọc sẽ hoạt động trong khi tài liệu vẫn hoạt động.
- Đôi khi, các đối tượng có thể được giữ lại theo ngữ cảnh trình gỡ lỗi và bảng điều khiển DevTools (ví dụ: sau khi đánh giá bảng điều khiển). Tạo ảnh chụp nhanh vùng nhớ khối xếp với bảng điều khiển rõ ràng và không có điểm ngắt đang hoạt động trong trình gỡ lỗi.
Biểu đồ bộ nhớ bắt đầu bằng một gốc, có thể là đối tượng window
của trình duyệt hoặc đối tượng Global
của mô-đun Node.js. Bạn không kiểm soát cách đối tượng gốc này được GC.
Mọi thứ không thể truy cập được từ thư mục gốc sẽ được GC.
Đối tượng giữ lại cây
Vùng nhớ khối xếp là một mạng lưới các đối tượng kết nối với nhau. Trong thế giới toán học, cấu trúc này được gọi là biểu đồ hoặc biểu đồ bộ nhớ. Biểu đồ được tạo từ các nút được kết nối bằng các cạnh, cả hai đều được gắn nhãn.
- Các nút (hoặc đối tượng) được gắn nhãn bằng tên của hàm hàm khởi tạo dùng để tạo các nút đó.
- Các cạnh được gắn nhãn bằng tên của các thuộc tính.
Tìm hiểu cách ghi hồ sơ bằng Trình phân tích mảng xếp. Một số điều hấp dẫn mà chúng ta có thể thấy trong bản ghi Trình phân tích vùng nhớ khối xếp sau đây bao gồm khoảng cách: khoảng cách từ gốc GC. Nếu hầu hết các đối tượng cùng loại ở cùng một khoảng cách và một vài đối tượng ở khoảng cách lớn hơn, thì đó là điều đáng điều tra.
Dominator
Đối tượng Dominator bao gồm một cấu trúc cây vì mỗi đối tượng có đúng một đối tượng Dominator. Đối tượng thống trị của một đối tượng có thể thiếu tham chiếu trực tiếp đến đối tượng mà đối tượng đó thống trị; tức là cây của đối tượng thống trị không phải là cây bao phủ của biểu đồ.
Trong sơ đồ sau:
- Nút 1 thống trị nút 2
- Nút 2 chi phối các nút 3, 4 và 6
- Nút 3 chi phối nút 5
- Nút 5 chiếm ưu thế so với nút 8
- Nút 6 chiếm ưu thế so với nút 7
Trong ví dụ sau, nút #3
là phần tử chi phối của #10
, nhưng #7
cũng tồn tại trong mọi đường dẫn đơn giản từ GC đến #10
. Do đó, đối tượng B là đối tượng chi phối đối tượng A nếu B tồn tại trong mọi đường dẫn đơn giản từ gốc đến đối tượng A.
Thông tin chi tiết về V8
Khi phân tích bộ nhớ, bạn nên hiểu lý do ảnh chụp nhanh vùng nhớ khối xếp có một số đặc điểm nhất định. Phần này mô tả một số chủ đề liên quan đến bộ nhớ, tương ứng cụ thể với máy ảo JavaScript V8 (máy ảo V8 hoặc máy ảo).
Biểu diễn đối tượng JavaScript
Có 3 loại dữ liệu gốc:
- Số (ví dụ: 3.14159..)
- Boolean (đúng hoặc sai)
- Chuỗi (ví dụ: 'Werner Heisenberg')
Các giá trị này không thể tham chiếu đến các giá trị khác và luôn là lá hoặc nút kết thúc.
Số có thể được lưu trữ dưới dạng:
- một giá trị số nguyên 31 bit tức thì được gọi là số nguyên nhỏ (SMIs) hoặc
- đối tượng vùng nhớ khối xếp, được gọi là số vùng nhớ khối xếp. Số vùng nhớ khối xếp được dùng để lưu trữ các giá trị không phù hợp với biểu mẫu SMI, chẳng hạn như double hoặc khi một giá trị cần được đóng hộp, chẳng hạn như đặt các thuộc tính trên đó.
Bạn có thể lưu trữ chuỗi trong:
- vùng nhớ khối xếp ảo hoặc
- bên ngoài trong bộ nhớ của trình kết xuất. Đối tượng trình bao bọc được tạo và dùng để truy cập vào bộ nhớ ngoài, chẳng hạn như lưu trữ nguồn tập lệnh và nội dung khác nhận được từ Web, thay vì sao chép vào vùng nhớ khối xếp của máy ảo.
Bộ nhớ cho các đối tượng JavaScript mới được phân bổ từ vùng nhớ khối xếp JavaScript chuyên dụng (hoặc vùng nhớ khối xếp ảo). Các đối tượng này được trình thu gom rác của V8 quản lý và do đó, sẽ vẫn tồn tại miễn là có ít nhất một tham chiếu mạnh đến các đối tượng đó.
Đối tượng gốc là mọi thứ khác không có trong vùng nhớ khối xếp JavaScript. Đối tượng gốc, trái ngược với đối tượng vùng nhớ khối xếp, không được trình thu gom rác V8 quản lý trong suốt thời gian hoạt động và chỉ có thể truy cập từ JavaScript bằng đối tượng trình bao bọc JavaScript.
Chuỗi Cons là một đối tượng bao gồm các cặp chuỗi được lưu trữ rồi nối với nhau và là kết quả của quá trình nối. Việc kết hợp nội dung chuỗi con chỉ xảy ra khi cần. Ví dụ: khi cần tạo một chuỗi con của một chuỗi đã nối.
Ví dụ: nếu nối a và b, bạn sẽ nhận được một chuỗi (a, b) đại diện cho kết quả của phép nối. Sau đó, nếu nối d với kết quả đó, bạn sẽ nhận được một chuỗi cons khác ((a, b), d).
Mảng – Mảng là một Đối tượng có khoá dạng số. Các mảng này được sử dụng rộng rãi trong máy ảo V8 để lưu trữ một lượng lớn dữ liệu. Các tập hợp cặp khoá-giá trị được dùng như từ điển được sao lưu bằng các mảng.
Một đối tượng JavaScript thông thường có thể là một trong hai loại mảng dùng để lưu trữ:
- thuộc tính được đặt tên và
- phần tử số
Trong trường hợp có rất ít thuộc tính, bạn có thể lưu trữ các thuộc tính đó trong chính đối tượng JavaScript.
Map (Bản đồ) – một đối tượng mô tả loại đối tượng và bố cục của đối tượng đó. Ví dụ: bản đồ được dùng để mô tả hệ phân cấp đối tượng ngầm ẩn cho việc truy cập nhanh vào thuộc tính.
Nhóm đối tượng
Mỗi nhóm đối tượng gốc bao gồm các đối tượng chứa các tệp tham chiếu lẫn nhau. Ví dụ: hãy xem xét một cây con DOM, trong đó mỗi nút đều có một đường liên kết đến nút mẹ và các đường liên kết đến nút con tiếp theo và nút con khác tiếp theo, do đó tạo thành một biểu đồ được kết nối. Xin lưu ý rằng các đối tượng gốc không được biểu thị trong vùng nhớ khối xếp JavaScript. Đó là lý do các đối tượng này có kích thước bằng 0. Thay vào đó, các đối tượng trình bao bọc sẽ được tạo.
Mỗi đối tượng trình bao bọc chứa một tệp tham chiếu đến đối tượng gốc tương ứng để chuyển hướng các lệnh đến đối tượng đó. Theo lượt, một nhóm đối tượng sẽ chứa các đối tượng trình bao bọc. Tuy nhiên, điều này không tạo ra một chu kỳ không thể thu thập, vì GC đủ thông minh để giải phóng các nhóm đối tượng có trình bao bọc không còn được tham chiếu. Tuy nhiên, nếu bạn quên phát hành một trình bao bọc, thì toàn bộ nhóm và các trình bao bọc liên kết sẽ bị giữ lại.