Tìm hiểu cách sử dụng Chrome và Công cụ của Chrome cho nhà phát triển để tìm các vấn đề về bộ nhớ ảnh hưởng đến hiệu suất trang, bao gồm cả tình trạng rò rỉ bộ nhớ, bộ nhớ bị đầy và việc thu gom rác thường xuyên.
Tóm tắt
- Tìm hiểu mức sử dụng bộ nhớ của trang bằng Trình quản lý tác vụ của Chrome.
- Hình dung mức sử dụng bộ nhớ theo thời gian bằng bản ghi Dòng thời gian.
- Xác định các cây DOM đã tách (một nguyên nhân phổ biến gây ra sự cố rò rỉ bộ nhớ) bằng Ảnh chụp nhanh vùng nhớ khối xếp.
- Tìm hiểu thời điểm bộ nhớ mới được phân bổ trong vùng nhớ khối xếp JS bằng bản ghi Dòng thời gian phân bổ.
- Xác định các phần tử đã tách được giữ lại bằng tham chiếu JavaScript.
Tổng quan
Theo tinh thần của mô hình hiệu suất RAIL, trọng tâm của các nỗ lực cải thiện hiệu suất phải là người dùng.
Các vấn đề về bộ nhớ rất quan trọng vì người dùng thường nhận thấy được. Người dùng có thể nhận thấy các vấn đề về bộ nhớ theo những cách sau:
- Hiệu suất của trang ngày càng giảm theo thời gian. Đây có thể là triệu chứng của tình trạng rò rỉ bộ nhớ. Sự cố rò rỉ bộ nhớ xảy ra khi một lỗi trên trang khiến trang sử dụng ngày càng nhiều bộ nhớ theo thời gian.
- Hiệu suất của một trang luôn kém. Đây có thể là triệu chứng của tình trạng tăng cường bộ nhớ. Tình trạng phồng bộ nhớ là khi một trang sử dụng nhiều bộ nhớ hơn mức cần thiết để có tốc độ trang tối ưu.
- Hiệu suất của trang bị chậm trễ hoặc có vẻ như thường xuyên tạm dừng. Đây có thể là triệu chứng của việc thu gom rác thường xuyên. Thu gom rác là khi trình duyệt lấy lại bộ nhớ. Trình duyệt sẽ quyết định thời điểm thực hiện việc này. Trong quá trình thu thập, tất cả các hoạt động thực thi tập lệnh sẽ bị tạm dừng. Vì vậy, nếu trình duyệt thu gom rác nhiều, quá trình thực thi tập lệnh sẽ bị tạm dừng nhiều.
Sự gia tăng bộ nhớ: "quá nhiều" là bao nhiêu?
Lỗi rò rỉ bộ nhớ rất dễ xác định. Nếu một trang web đang sử dụng ngày càng nhiều bộ nhớ, thì tức là bạn đã bị rò rỉ bộ nhớ. Tuy nhiên, việc xác định tình trạng tăng cường bộ nhớ khó khăn hơn một chút. Điều gì được coi là "sử dụng quá nhiều bộ nhớ"?
Không có con số cụ thể nào ở đây vì các thiết bị và trình duyệt khác nhau có các chức năng khác nhau. Cùng một trang chạy mượt mà trên điện thoại thông minh cao cấp có thể gặp sự cố trên điện thoại thông minh cấp thấp.
Điểm mấu chốt ở đây là sử dụng mô hình RAIL và tập trung vào người dùng. Tìm hiểu những thiết bị phổ biến với người dùng của bạn, sau đó kiểm thử trang của bạn trên những thiết bị đó. Nếu trải nghiệm luôn xấu, thì trang có thể đang vượt quá dung lượng bộ nhớ của các thiết bị đó.
Giám sát mức sử dụng bộ nhớ theo thời gian thực bằng Trình quản lý tác vụ của Chrome
Hãy sử dụng Trình quản lý tác vụ của Chrome làm điểm khởi đầu để điều tra vấn đề về bộ nhớ. Trình quản lý tác vụ là một trình giám sát theo thời gian thực cho bạn biết mức sử dụng bộ nhớ của một trang.
Nhấn tổ hợp phím Shift+Esc hoặc chuyển đến trình đơn chính của Chrome rồi chọn Công cụ khác > Trình quản lý tác vụ để mở Trình quản lý tác vụ.
Nhấp chuột phải vào tiêu đề bảng của Trình quản lý tác vụ rồi bật JavaScript memory (Bộ nhớ JavaScript).
Hai cột này cho bạn biết nhiều thông tin về cách trang của bạn đang sử dụng bộ nhớ:
- Cột Mức sử dụng bộ nhớ thể hiện bộ nhớ hệ điều hành. Các nút DOM được lưu trữ trong bộ nhớ của hệ điều hành. Nếu giá trị này đang tăng, các nút DOM sẽ được tạo.
Cột Bộ nhớ JavaScript biểu thị vùng nhớ khối xếp JS. Cột này chứa hai giá trị. Giá trị bạn quan tâm là số trực tiếp (số trong ngoặc đơn). Số trực tiếp thể hiện dung lượng bộ nhớ mà các đối tượng có thể truy cập trên trang của bạn đang sử dụng. Nếu con số này tăng lên, thì có thể đối tượng mới đang được tạo hoặc đối tượng hiện có đang tăng lên.
Trực quan hoá sự cố rò rỉ bộ nhớ bằng tính năng Bản ghi hiệu suất
Bạn cũng có thể sử dụng bảng điều khiển Hiệu suất làm điểm khởi đầu khác trong quá trình điều tra. Bảng điều khiển Hiệu suất giúp bạn hình dung mức sử dụng bộ nhớ của một trang theo thời gian.
- Mở bảng điều khiển Hiệu suất trong Công cụ cho nhà phát triển.
- Bật hộp đánh dấu Memory (Bộ nhớ).
- Ghi âm.
Để minh hoạ bản ghi bộ nhớ Hiệu suất, hãy xem xét mã sau:
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);
Mỗi khi nhấn nút được tham chiếu trong mã, 10.000 nút div
sẽ được thêm vào phần nội dung tài liệu và một chuỗi gồm một triệu ký tự x
sẽ được đẩy vào mảng x
.
Khi chạy mã này, bạn sẽ có một bản ghi Dòng thời gian như ảnh chụp màn hình sau:
Trước tiên, hãy giải thích về giao diện người dùng. Biểu đồ HEAP trong ngăn Overview (Tổng quan) (dưới NET) thể hiện vùng nhớ khối xếp JS. Bên dưới ngăn Tổng quan là ngăn Bộ đếm. Tại đây, bạn có thể thấy mức sử dụng bộ nhớ được phân tích theo vùng nhớ khối xếp JS (tương tự như biểu đồ HEAP trong ngăn Overview (Tổng quan)), tài liệu, nút DOM, trình nghe và bộ nhớ GPU. Khi bạn tắt một hộp đánh dấu, hộp đánh dấu đó sẽ bị ẩn khỏi biểu đồ.
Bây giờ, hãy phân tích mã so với ảnh chụp màn hình. Nếu xem bộ đếm nút (biểu đồ màu xanh lục), bạn có thể thấy bộ đếm này khớp chính xác với mã. Số lượng nút tăng lên theo các bước riêng biệt. Bạn có thể giả định rằng mỗi lần tăng số lượng nút là một lệnh gọi đến grow()
. Biểu đồ vùng nhớ khối xếp JS (biểu đồ màu xanh dương) không đơn giản như vậy. Theo các phương pháp hay nhất, mức giảm đầu tiên thực sự là một lần thu gom rác bắt buộc (được thực hiện bằng cách nhấn nút thu gom rác). Khi quá trình ghi diễn ra, bạn có thể thấy kích thước vùng nhớ khối xếp JS tăng đột biến. Điều này là tự nhiên và như dự kiến: mã JavaScript đang tạo các nút DOM trên mỗi lượt nhấp vào nút và thực hiện nhiều thao tác khi tạo chuỗi gồm một triệu ký tự. Điều quan trọng ở đây là thực tế là vùng nhớ khối xếp JS kết thúc ở mức cao hơn so với khi bắt đầu ("bắt đầu" ở đây là điểm sau khi thu thập rác bắt buộc). Trong thực tế, nếu bạn thấy mẫu tăng kích thước vùng nhớ khối xếp JS hoặc kích thước nút này, thì điều đó có thể có nghĩa là rò rỉ bộ nhớ.
Khám phá các sự cố rò rỉ bộ nhớ cây DOM đã tách bằng tính năng Ảnh chụp nhanh vùng nhớ khối xếp
Bạn chỉ có thể thu gom rác cho một nút DOM khi không có tham chiếu nào đến nút đó từ cây DOM hoặc mã JavaScript của trang. Một nút được cho là "được tách ra" khi nút đó bị xoá khỏi cây DOM nhưng một số JavaScript vẫn tham chiếu đến nút đó. Các nút DOM đã tách là nguyên nhân phổ biến gây ra sự cố rò rỉ bộ nhớ. Phần này hướng dẫn bạn cách sử dụng trình phân tích tài nguyên vùng nhớ khối xếp của DevTools để xác định các nút đã tách.
Dưới đây là ví dụ đơn giản về các nút DOM được tách rời.
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);
Khi nhấp vào nút được tham chiếu trong mã, một nút ul
sẽ được tạo với 10 nút con li
. Các nút này được mã tham chiếu nhưng không tồn tại trong cây DOM, vì vậy, các nút này sẽ bị tách rời.
Ảnh chụp nhanh vùng nhớ khối xếp là một cách để xác định các nút đã tách. Như tên gọi, ảnh chụp nhanh vùng nhớ khối xếp cho bạn biết cách bộ nhớ được phân phối giữa các đối tượng JS và nút DOM của trang tại thời điểm chụp nhanh.
Để tạo ảnh chụp nhanh, hãy mở DevTools rồi chuyển đến bảng điều khiển Memory (Bộ nhớ), chọn nút chọn Heap Snapshot (Ảnh chụp nhanh vùng nhớ khối xếp) rồi nhấn nút Take snapshot (Lưu toàn cảnh).
Ảnh chụp nhanh có thể mất chút thời gian để xử lý và tải. Sau khi hoàn tất, hãy chọn tệp đó trong bảng điều khiển bên trái (có tên là Snapshot Heap (Bản tổng quan nhanh về vùng nhớ khối xếp)).
Nhập Detached
vào hộp nhập Bộ lọc lớp để tìm kiếm các cây DOM được tách rời.
Mở rộng dấu carat để kiểm tra cây đã tách.
Nhấp vào một nút để tìm hiểu thêm về nút đó. Trong ngăn Objects (Đối tượng), bạn có thể xem thêm thông tin về mã đang tham chiếu đến đối tượng đó. Ví dụ: trong ảnh chụp màn hình sau, bạn có thể thấy rằng biến detachedTree
đang tham chiếu đến nút. Để khắc phục sự cố rò rỉ bộ nhớ cụ thể này, bạn sẽ nghiên cứu mã sử dụng detachedTree
và đảm bảo rằng mã đó xoá tham chiếu đến nút khi không còn cần thiết.
Xác định tình trạng rò rỉ bộ nhớ vùng nhớ khối xếp JS bằng Tiến trình phân bổ
Allocation Timeline (Dòng thời gian phân bổ) là một công cụ khác có thể giúp bạn theo dõi sự cố rò rỉ bộ nhớ trong vùng nhớ khối xếp JS.
Để minh hoạ Tiến trình phân bổ, hãy xem xét mã sau:
var x = [];
function grow() {
x.push(new Array(1000000).join('x'));
}
document.getElementById('grow').addEventListener('click', grow);
Mỗi khi nút được tham chiếu trong mã được nhấn, một chuỗi gồm một triệu ký tự sẽ được thêm vào mảng x
.
Để ghi Allocation Timeline (Tiến trình phân bổ), hãy mở DevTools, chuyển đến bảng điều khiển Memory (Bộ nhớ), chọn nút chọn Allocations on timeline (Phân bổ trên tiến trình), nhấn nút Record (Ghi)
, thực hiện hành động mà bạn nghi ngờ là nguyên nhân gây ra sự cố rò rỉ bộ nhớ, sau đó nhấn nút Stop recording (Dừng ghi) khi bạn hoàn tất.Khi bạn đang ghi, hãy chú ý xem có thanh màu xanh dương nào xuất hiện trên Allocation Timeline (Dòng thời gian phân bổ) hay không, như trong ảnh chụp màn hình sau.
Các thanh màu xanh dương đó thể hiện mức phân bổ bộ nhớ mới. Những mức phân bổ bộ nhớ mới đó là các ứng cử viên cho sự cố rò rỉ bộ nhớ. Bạn có thể phóng to một thanh để lọc ngăn Constructor (Hàm khởi tạo) để chỉ hiển thị các đối tượng được phân bổ trong khung thời gian đã chỉ định.
Mở rộng đối tượng và nhấp vào giá trị của đối tượng đó để xem thêm thông tin chi tiết về đối tượng đó trong ngăn Object (Đối tượng). Ví dụ: trong ảnh chụp màn hình bên dưới, bằng cách xem thông tin chi tiết về đối tượng mới được phân bổ, bạn có thể thấy đối tượng đó được phân bổ cho biến x
trong phạm vi Window
.
Điều tra hoạt động phân bổ bộ nhớ theo hàm
Sử dụng loại hồ sơ Lấy mẫu mức phân bổ trong bảng điều khiển Bộ nhớ để xem mức phân bổ bộ nhớ theo hàm JavaScript.
- Chọn nút chọn Allocation sampling (Lấy mẫu mức phân bổ). Nếu có một worker trên trang, bạn có thể chọn worker đó làm mục tiêu phân tích tài nguyên trong cửa sổ Select JavaScript VM instance (Chọn thực thể máy ảo JavaScript).
- Nhấn nút Start (Bắt đầu).
- Thực hiện các hành động trên trang mà bạn muốn kiểm tra.
- Nhấn nút Dừng khi bạn đã hoàn tất tất cả thao tác.
Công cụ phát triển cho bạn thấy thông tin chi tiết về mức phân bổ bộ nhớ theo hàm. Chế độ xem mặc định là Nặng (Từ dưới lên), hiển thị các hàm đã phân bổ nhiều bộ nhớ nhất ở trên cùng.
Xác định các đối tượng được giữ lại bằng tham chiếu JS
Cấu hình Các phần tử đã tách cho bạn thấy các phần tử đã tách vẫn tồn tại vì được mã JavaScript tham chiếu.
Ghi lại hồ sơ Các phần tử đã tách để xem chính xác số lượng nút và nút HTML.
Phát hiện các lần thu gom rác thường xuyên
Nếu trang của bạn thường xuyên tạm dừng, thì có thể bạn gặp vấn đề về việc thu gom rác.
Bạn có thể sử dụng Trình quản lý tác vụ của Chrome hoặc bản ghi bộ nhớ Dòng thời gian để phát hiện các hoạt động thu thập rác thường xuyên. Trong Trình quản lý tác vụ, các giá trị Bộ nhớ hoặc Bộ nhớ JavaScript thường xuyên tăng và giảm cho biết việc thu thập rác thường xuyên. Trong bản ghi Dòng thời gian, biểu đồ số lượng nút hoặc vùng nhớ khối xếp JS thường xuyên tăng và giảm cho biết việc thu thập rác thường xuyên.
Sau khi xác định được vấn đề, bạn có thể sử dụng bản ghi Tiến trình phân bổ để tìm hiểu vị trí phân bổ bộ nhớ và những hàm nào đang gây ra hoạt động phân bổ.