Khắc phục sự cố về bộ nhớ

Tìm hiểu cách sử dụng Chrome và Công cụ 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 rò rỉ bộ nhớ, bộ nhớ đầy và thường xuyên thu gom rác.

Tóm tắt

  • Tìm hiểu xem trang của bạn hiện đang sử dụng bao nhiêu bộ nhớ thông qua Trình quản lý tác vụ Chrome.
  • Trực quan hoá mức sử dụng bộ nhớ theo thời gian bằng các bản ghi trên Dòng thời gian.
  • Xác định các cây DOM tách rời (nguyên nhân phổ biến gây ra rò rỉ bộ nhớ) bằng Ảnh chụp nhanh của 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 của bạn nhờ các bản ghi của Dòng thời gian phân bổ.

Tổng quan

Theo tinh thần của mô hình hiệu suất RAIL, trọng tâm trong các nỗ lực về hiệu suất của bạn nên là người dùng của bạn.

Các vấn đề về bộ nhớ là vấn đề quan trọng vì người dùng thường nhận biết được. Người dùng có thể nhận biết bộ nhớ vấn đề theo những cách sau:

  • Hiệu suất của trang ngày càng giảm dần theo thời gian. Đây có thể là triệu chứng của rò rỉ bộ nhớ. Rò rỉ bộ nhớ xảy ra khi một lỗi trên trang khiến trang ngày càng sử dụng nhiều và bộ nhớ lớn hơn theo thời gian.
  • Hiệu suất của trang liên tục ở mức thấp. Đây có thể là triệu chứng của bộ nhớ đầy. Bộ nhớ Vượt quá 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 một trang bị chậm trễ hoặc có vẻ thường xuyên tạm dừng. Đây có thể là triệu chứng của thường xuyên thu gom rác. Thu gom rác là khi trình duyệt lấy lại bộ nhớ. Trình duyệt quyết định thời điểm điều này xảy ra. Trong lúc thu thập, mọi quá trình thực thi tập lệnh đều bị tạm dừng. Vì vậy, nếu trình duyệt đang thu thập rác rất nhiều, việc thực thi tập lệnh sẽ bị tạm dừng rất nhiều.

Bộ nhớ đầy: "quá nhiều" là bao nhiêu?

Rò rỉ bộ nhớ rất dễ xác định. Nếu một trang web ngày càng sử dụng nhiều bộ nhớ, có nghĩa là bị rò rỉ. Tuy nhiên, việc xác định tình trạng quá tải bộ nhớ sẽ khó khăn hơn một chút. Những nội dung nào được xem là "sử dụng quá nhiều bộ nhớ"?

Không có con số cố định ở đây, vì các thiết bị và trình duyệt khác nhau có những khả năng khác nhau. Cùng một trang chạy trơn tru 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ều quan trọng ở đây là sử dụng mô hình RAIL và tập trung vào người dùng của bạn. Tìm hiểu những thiết bị phổ biến với người dùng của bạn rồi thử nghiệm trang của bạn trên các thiết bị đó. Nếu trải nghiệm nhất quán trang có thể vượt quá khả nă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ụ Chrome

Hãy bắt đầu điều tra vấn đề về bộ nhớ bằng Trình quản lý tác vụ Chrome. Trình quản lý tác vụ là trình theo dõi thời gian thực cho bạn biết một trang hiện đang sử dụng bao nhiêu bộ nhớ.

  1. Nhấn 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ụ thành mở Trình quản lý tác vụ.

    Mở Trình quản lý tác vụ

  2. Nhấp chuột phải vào tiêu đề bảng của Trình quản lý tác vụ và bật Bộ nhớ JavaScript.

    Bật bộ nhớ JS

Hai cột này cho bạn biết các thông tin khác nhau về cách trang của bạn đang sử dụng bộ nhớ:

  • Cột Memory (Bộ nhớ) biểu thị bộ nhớ gốc. Các nút DOM được lưu trữ trong bộ nhớ gốc. Nếu trường hợp này giá trị sẽ tăng lên, 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 2 giá trị. Chiến lược phát hành đĩa đơn giá trị mà bạn quan tâm là số trực tiếp (số trong ngoặc đơn). Số điện thoại đang hoạt động thể hiện dung lượng bộ nhớ mà các đối tượng có thể truy cập trên trang đang sử dụng. Nếu số này là tăng lên, đối tượng mới được tạo hoặc đối tượng hiện có đang phát triển.

Trực quan hoá tình trạng rò rỉ bộ nhớ bằ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 một điểm xuất phát khác trong quá trình điều tra. 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.

  1. Mở bảng điều khiển Hiệu suất trên Công cụ cho nhà phát triển.
  2. Bật hộp đánh dấu Memory (Bộ nhớ).
  3. Tạo bản ghi âm.

Để minh hoạ các bản ghi Bộ nhớ về hiệu suất, hãy xem xét mã dưới đây:

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 người dùng nhấn nút tham chiếu trong mã, 10.000 nút div sẽ được thêm vào 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. Việc chạy mã này sẽ tạo ra một bản ghi Dòng thời gian như ảnh chụp màn hình sau đây:

ví dụ về tăng trưởng đơn giản

Đầu tiên là nội dung giải thích về giao diện người dùng. Biểu đồ HEAP trong ngăn Overview (Tổng quan) (bên dưới NET) biểu thị vùng nhớ khối xếp JS. Bên dưới ngăn Overview (Tổng quan) là ngăn Counter (Bộ đếm). Tại đây, bạn có thể xem 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. Việc vô hiệu hoá một hộp đánh dấu sẽ ẩn hộp đánh dấu đó khỏi biểu đồ.

Bây giờ là một bản phân tích mã so với ảnh chụp màn hình. Nếu bạn nhìn vào bộ đếm nút ( biểu đồ màu xanh lục) bạn có thể thấy rằng mã đó khớp hoàn toàn với mã. Số lượng nút tăng theo các bước rời rạc. Bạn có thể giả định rằng mỗi mức tăng trong số nút là một lệnh gọi đến grow(). JS biểu đồ vùng nhớ khối xếp (biểu đồ màu xanh dương) không đơn giản. Để tuân thủ các phương pháp hay nhất, lần giảm đầu tiên thực ra là thu gom rác bắt buộc (đạt được bằng cách nhấn nút thu thập rác). Như bạn có thể thấy kích thước vùng nhớ khối xếp JS tăng đột biến. Đây là điều tự nhiên và được mong đợi: Mã JavaScript đang tạo các nút DOM trên mỗi nhấp chuột vào nút và thực hiện rất nhiều việc khi mã sẽ tạo chuỗi một triệu ký tự. Điều quan trọng ở đây là vùng nhớ khối xếp JS kết thúc cao hơn so với ban đầu ("ban đầu" ở đây là thời điểm sau khi thu gom rác bắt buộc). Trong trong thế giới thực, nếu bạn thấy mô hình tăng kích thước vùng nhớ khối xếp JS hoặc kích thước nút này, có thể là do rò rỉ bộ nhớ.

Khám phá các trường hợp rò rỉ bộ nhớ cây DOM tách biệt với Ảnh chụp nhanh vùng nhớ khối xếp

Một nút DOM chỉ có thể được thu thập rác khi không có tham chiếu nào đến nút đó từ Cây DOM hoặc mã JavaScript. Một nút được gọi là "phân tách" khi bị xoá khỏi cây DOM nhưng một số JavaScript vẫn tham chiếu đến hàm đó. Các nút DOM tách biệt là nguyên nhân phổ biến gây ra rò rỉ bộ nhớ. Chiến dịch này hướng dẫn bạn cách sử dụng Công cụ cho nhà phát triển Trình phân tích vùng nhớ khối xếp để xác định các nút đã tách rời.

Dưới đây là một ví dụ đơn giản về các nút DOM được tách riêng.

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);

Thao tác nhấp vào nút được tham chiếu trong mã sẽ tạo một nút ul có 10 phần tử con li. Các nút này được tham chiếu theo mã nhưng không tồn tại trong cây DOM, vì vậy chúng được tách ra.

Ảnh chụp nhanh của vùng nhớ khối xếp là một cách để xác định các nút đã tách. Như chính tên gọi của nó, ảnh chụp nhanh của 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 trên trang của bạn tại thời điểm .

Để tạo bản tổng quan nhanh, hãy mở Công cụ cho nhà phát triển và chuyển đến bảng điều khiển Memory (Bộ nhớ), chọn Heap (Vùng nhớ khối xếp) Nút chọn Chụp nhanh, sau đó nhấn nút Chụp nhanh.

chụp nhanh vùng nhớ khối xếp

Bản tổng quan 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 thẻ ở bên trái bảng điều khiển (có tên là HEAP SNAPSHOTS).

Nhập Detached vào hộp văn bản Class filter (Bộ lọc lớp) để tìm kiếm các cây DOM riêng biệt.

lọc cho các nút tách rời

Mở rộng các cara để kiểm tra một cây riêng biệt.

điều tra cây tách rời

Các nút được đánh dấu màu vàng có thông tin tham chiếu trực tiếp đến chúng từ mã JavaScript. Các nút được làm nổi bật màu đỏ không có tham chiếu trực tiếp. Chúng chỉ còn sống vì chúng là một phần của nút màu vàng cây xanh. Nói chung, bạn muốn tập trung vào các nút màu vàng. Sửa mã để nút màu vàng không tồn tại lâu hơn cần thiết, đồng thời bạn cũng loại bỏ các nút màu đỏ là một phần của của nút màu vàng.

Hãy nhấp vào một nút màu vàng để tìm hiểu thêm. Trong ngăn Objects (Đối tượng), bạn có thể thấy thêm về mã đang tham chiếu đến thông tin đó. Ví dụ: trong ảnh chụp màn hình bên dưới, 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ã này xoá tham chiếu đến nút khi không còn cần thiết nữa.

đang điều tra một nút màu vàng

Xác định lỗi rò rỉ bộ nhớ vùng nhớ khối xếp JS bằng Dòng thời gian phân bổ

Allocation Time (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 tình trạng 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 đoạn mã sau:

var x = [];

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

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

Mỗi lần nút tham chiếu trong mã được đẩy, một chuỗi gồm một triệu ký tự sẽ được được thêm vào mảng x.

Để ghi lại Dòng thời gian phân bổ, hãy mở Công cụ cho nhà phát triển, chuyển đến bảng điều khiển Hồ sơ, chọn mục Ghi lại Nút chọn Dòng thời gian phân bổ, nhấn nút Bắt đầu, thực hiện thao tác mà bạn nghi ngờ đang gây ra sự cố rò rỉ bộ nhớ, sau đó nhấn nút stop recording (dừng ghi) (nút dừng ghi âm) khi thế là xong.

Khi bạn đang ghi lại, hãy chú ý xem có thanh màu xanh dương nào xuất hiện trên Dòng thời gian phân bổ hay không, chẳng hạn như trong ảnh chụp màn hình dưới đây.

lượt phân bổ mới

Những thanh màu xanh dương đó đại diện cho các cơ cấu phân bổ bộ nhớ mới. Những cơ cấu phân bổ bộ nhớ mới đó là đề xuất của bạn để phát hiện rò rỉ bộ nhớ. Bạn có thể phóng to một thanh để lọc ngăn Hàm khởi tạo sao cho chỉ hiển thị các đối tượng được phân bổ trong khung thời gian quy định.

tiến trình phân bổ thu phóng

Mở rộng đối tượng rồi 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). Cho 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 dữ liệu này được phân bổ cho biến x trong phạm vi Window.

thông tin chi tiết về đối tượng

Kiểm tra quy trình phân bổ bộ nhớ theo chức năng

Sử dụng loại Allocation Sample (Lấy mẫu phân bổ) trong bảng điều khiển Memory (Bộ nhớ) để xem quá trình phân bổ bộ nhớ theo hàm JavaScript.

Trình phân tích mức phân bổ bản ghi

  1. Chọn nút chọn Phân bổ lấy mẫu. Nếu có một worker trên trang, bạn bạn có thể chọn mục đó làm mục tiêu phân tích tài nguyên bằng cách sử dụng trình đơn thả xuống bên cạnh nút Start (Bắt đầu).
  2. Nhấn nút Start (Bắt đầu).
  3. Thực hiện các thao tác trên trang mà bạn muốn điều tra.
  4. Nhấn vào nút Dừng khi bạn đã hoàn tất tất cả các thao tác của mình.

Công cụ cho nhà phát triển cho bạn thấy thông tin chi tiết về hoạt động phân bổ bộ nhớ theo chức năng. Chế độ xem mặc định là Nặng (Dưới cùng) Up), cho thấy các hàm được phân bổ nhiều bộ nhớ nhất ở trên cùng.

Hồ sơ phân bổ

Phát hiện các hoạt động thu gom rác thường xuyên

Nếu trang của bạn có vẻ tạm dừng thường xuyên, thì bạn có thể đang gặp sự cố thu thập rác.

Bạn có thể sử dụng Trình quản lý tác vụ Chrome hoặc bản ghi bộ nhớ Dòng thời gian để phát hiện rác thường xuyên bộ sưu tập. Trong Trình quản lý tác vụ, Bộ nhớ hoặc Bộ nhớ JavaScript thường xuyên tăng và giảm biểu thị việc thu gom rác thường xuyên. Trong các bản ghi trên Dòng thời gian, các bản ghi này thường tăng và giảm Biểu đồ số lượng nút hoặc vùng nhớ khối xếp JS cho biết việc thu gom rác thường xuyên.

Sau khi xác định được vấn đề, bạn có thể sử dụng bản ghi Dòng thời gian phân bổ để tìm hiểu bộ nhớ đang được phân bổ và hàm nào gây ra việc phân bổ.