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 cả việc rò rỉ bộ nhớ, đầy bộ nhớ và thường xuyên thu thập 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 của các nỗ lực về hiệu suất phải là người dùng.

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 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 dần theo thời gian. Đây có thể là dấu hiệu của việc 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 bộ nhớ 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. Phồng bộ nhớ xảy ra 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à dấu hiệu 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 đ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 thu thập nhiều dữ liệu rác, thì quá trình 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ớ, thì tức là bạn đã gặp phải sự cố rò rỉ bộ nhớ. 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 xem những thiết bị nào phổ biến với người dùng của bạn và kiểm tra trang của bạn trên những thiết bị đó. Nếu trải nghiệm liên tục không tốt, thì 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à công cụ theo dõi theo thời gian thực cho biết mức bộ nhớ mà một trang hiện đang sử dụng.

  1. 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ụ.

    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 giá trị này 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 2 giá trị. Giá trị bạn quan tâm là số thực (số trong dấu ngoặc đơn). Số trực tiếp thể hiện dung lượng bộ nhớ mà các đối tượng có thể tiếp cận trên trang của bạn đang sử dụng. Nếu con số này tăng lên, thì các đối tượng mới sẽ được tạo hoặc các đối tượng hiện có sẽ tăng lê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. Bảng điều khiển Hiệu suất giúp bạn trực quan hoá 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 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 nhìn vào bộ đếm nút (biểu đồ màu xanh lục), bạn có thể thấy bộ đếm nút này khớp hoàn toàn với mã. Số lượng nút tăng lên theo từng bước riêng biệt. 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(). Biểu đồ vùng nhớ khối xếp JS (biểu đồ màu xanh dương) không đơn giản. Để phù hợp với các phương pháp hay nhất, bước nhúng đầu tiên thực ra là quá trình thu gom rác bắt buộc (đạt được bằng cách nhấn nút thu thập rác). Khi quá trình ghi tiến trình, 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à bình thường và được mong đợi: mã JavaScript sẽ tạo các nút DOM trên mỗi lần nhấp vào nút và thực hiện rất nhiều thao tác khi tạo một chuỗi gồm 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 ("khởi đầu" ở đây là thời điểm sau khi thu gom rác bắt buộc). Trong thực tế, 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, thì có thể đó là tình trạng 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ó thông tin tham chiếu đến nút đó từ cây DOM hoặc mã JavaScript của trang. Một nút được cho 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 nút đó. Các nút DOM tách biệt là nguyên nhân phổ biến gây ra 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 vùng nhớ khối xếp của Công cụ cho nhà phát triển để xác định các nút đã tách.

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 mã tham chiếu 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, bản tổng quan 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 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 nút chọn HeapSnapshot (Chụp nhanh), sau đó nhấn nút Take Snapshot (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 biểu tượng đó trong bảng điều khiển bên trái (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 đánh dấu màu đỏ không có tệp tham chiếu trực tiếp. Các nút này chỉ còn hoạt động vì thuộc cây của nút màu vàng. Nói chung, bạn muốn tập trung vào các nút màu vàng. Hãy sửa mã của bạn để nút màu vàng không tồn tại lâu hơn mức cần thiết, đồng thời bạn cũng loại bỏ các nút màu đỏ thuộc cây 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ể 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 bên dưới, bạn có thể thấy 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 nên nghiên cứu mã sử dụng detachedTree và đảm bảo rằng mã này sẽ xoá thông tin tham chiếu đến nút khi không còn cần đến.

đ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 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 Profiles (Hồ sơ), chọn nút chọn Record Allocation flow (Ghi lại dòng thời gian phân bổ), nhấn nút Start (Bắt đầu), thực hiện hành động mà bạn nghi ngờ là gây ra sự cố rò rỉ bộ nhớ, sau đó nhấn nút stop recording (nút dừng ghi âm) 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 Dòng thời gian phân bổ hay không, 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 đó chính là đề xuất cho việc rò rỉ bộ nhớ. Bạn có thể thu phóng 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 chỉ đị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). 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 rằng đối tượng đó đã đượ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 có thể chọn worker đó 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 (Từ dưới lên), cho thấy các hàm đã 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ụ của Chrome hoặc các bản ghi bộ nhớ trên Dòng thời gian để phát hiện việc thu gom 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 đại diện cho việc thu gom rác thường xuyên. Trong các 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 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 vị trí bộ nhớ đang được phân bổ và hàm nào gây ra việc phân bổ.