Rò rỉ bộ nhớ cửa sổ tách biệt

Tìm và khắc phục những sự cố rò rỉ bộ nhớ phức tạp do các cửa sổ bị tách ra.

Bartek Nowierski
Bartek Nowierski

Rò rỉ bộ nhớ trong JavaScript là gì?

Rò rỉ bộ nhớ là hiện tượng tăng không chủ ý dung lượng bộ nhớ mà một ứng dụng sử dụng theo thời gian. Trong JavaScript, rò rỉ bộ nhớ xảy ra khi đối tượng không còn cần thiết nữa, nhưng vẫn được các hàm hoặc đối tượng khác tham chiếu đến. Các tham chiếu này ngăn trình thu gom rác thu hồi các đối tượng không cần thiết.

Công việc của trình thu gom rác là xác định và lấy lại các đối tượng không truy cập được qua ứng dụng nữa. Phương thức này hoạt động ngay cả khi các đối tượng tham chiếu đến chính chúng hoặc tham chiếu theo chu kỳ – khi không còn tham chiếu nào mà qua đó một ứng dụng có thể truy cập vào một nhóm đối tượng, ứng dụng đó có thể được thu gom rác.

let A = {};
console.log(A); // local variable reference

let B = {A}; // B.A is a second reference to A

A = null; // unset local variable reference

console.log(B.A); // A can still be referenced by B

B.A = null; // unset B's reference to A

// No references to A are left. It can be garbage collected.

Một lớp rò rỉ bộ nhớ đặc biệt phức tạp xảy ra khi một ứng dụng tham chiếu đến các đối tượng có vòng đời riêng, chẳng hạn như các phần tử DOM hoặc cửa sổ bật lên. Các loại đối tượng này có thể không được sử dụng mà ứng dụng không biết, nghĩa là mã xử lý ứng dụng có thể có các tham chiếu còn lại duy nhất đến một đối tượng có thể bị thu thập rác.

Cửa sổ rời là gì?

Trong ví dụ sau, ứng dụng trình xem bản trình chiếu có các nút để mở và đóng cửa sổ bật lên ghi chú của người trình bày. Hãy tưởng tượng một người dùng nhấp vào Show Notes (Hiện ghi chú), sau đó trực tiếp đóng cửa sổ bật lên thay vì nhấp vào nút Hide Notes (Ẩn ghi chú) – biến notesWindow vẫn tham chiếu đến cửa sổ bật lên có thể truy cập được, mặc dù cửa sổ bật lên không còn được sử dụng nữa.

<button id="show">Show Notes</button>
<button id="hide">Hide Notes</button>
<script type="module">
  let notesWindow;
  document.getElementById('show').onclick = () => {
    notesWindow = window.open('/presenter-notes.html');
  };
  document.getElementById('hide').onclick = () => {
    if (notesWindow) notesWindow.close();
  };
</script>

Đây là ví dụ về một cửa sổ đã tách. Cửa sổ bật lên đã đóng, nhưng mã của chúng ta có tham chiếu đến mã đó để ngăn trình duyệt không thể huỷ bỏ và lấy lại bộ nhớ đó.

Khi một trang gọi window.open() để tạo thẻ hoặc cửa sổ trình duyệt mới, đối tượng Window sẽ được trả về đại diện cho cửa sổ hoặc thẻ. Ngay cả sau khi bạn đóng một cửa sổ như vậy hoặc người dùng đã di chuyển khỏi cửa sổ đó, thì đối tượng Window được trả về từ window.open() vẫn có thể được dùng để truy cập thông tin về cửa sổ đó. Đây là một loại cửa sổ đã tách: vì mã JavaScript vẫn có thể truy cập vào các thuộc tính trên đối tượng Window đã đóng, nên mã này phải được lưu trong bộ nhớ. Nếu cửa sổ bao gồm nhiều đối tượng JavaScript hoặc iframe, thì bạn không thể lấy lại bộ nhớ đó cho đến khi không còn tham chiếu JavaScript nào đến các thuộc tính của cửa sổ.

Sử dụng Công cụ của Chrome cho nhà phát triển để minh hoạ cách giữ lại tài liệu sau khi đóng cửa sổ.

Vấn đề tương tự cũng có thể xảy ra khi sử dụng các phần tử <iframe>. Iframe hoạt động như các cửa sổ lồng nhau chứa tài liệu và thuộc tính contentWindow của iframe đó cung cấp quyền truy cập vào đối tượng Window chứa, giống như giá trị được window.open() trả về. Mã JavaScript có thể tham chiếu đến contentWindow hoặc contentDocument của iframe ngay cả khi iframe đó bị xoá khỏi DOM hoặc URL của iframe đó thay đổi. Điều này ngăn tài liệu bị thu thập rác vì bạn vẫn có thể truy cập vào các thuộc tính của tài liệu đó.

Hình minh hoạ cách trình xử lý sự kiện có thể giữ lại tài liệu của iframe, ngay cả sau khi điều hướng iframe đến một URL khác.

Trong trường hợp một tham chiếu đến document trong một cửa sổ hoặc iframe được giữ lại từ JavaScript, tài liệu đó sẽ được lưu trong bộ nhớ ngay cả khi cửa sổ chứa hoặc iframe đó điều hướng đến URL mới. Điều này có thể đặc biệt rắc rối khi JavaScript lưu giữ tham chiếu đó không phát hiện được rằng cửa sổ/khung đã điều hướng đến một URL mới, vì không biết khi nào nó trở thành tham chiếu cuối cùng lưu giữ một tài liệu trong bộ nhớ.

Cách các cửa sổ bị tách gây ra rò rỉ bộ nhớ

Khi làm việc với cửa sổ và iframe trên cùng một miền với trang chính, bạn thường sẽ theo dõi các sự kiện hoặc truy cập vào các thuộc tính trên ranh giới tài liệu. Ví dụ: hãy xem lại một biến thể trong ví dụ về trình xem bản trình bày từ đầu hướng dẫn này. Trình xem sẽ mở ra một cửa sổ thứ hai để hiển thị ghi chú của người thuyết trình. Cửa sổ ghi chú của người thuyết trình theo dõi các sự kiện click làm tín hiệu để chuyển sang trang trình bày tiếp theo. Nếu người dùng đóng cửa sổ ghi chú này, JavaScript chạy trong cửa sổ mẹ ban đầu vẫn có quyền truy cập đầy đủ vào tài liệu ghi chú của người thuyết trình:

<button id="notes">Show Presenter Notes</button>
<script type="module">
  let notesWindow;
  function showNotes() {
    notesWindow = window.open('/presenter-notes.html');
    notesWindow.document.addEventListener('click', nextSlide);
  }
  document.getElementById('notes').onclick = showNotes;

  let slide = 1;
  function nextSlide() {
    slide += 1;
    notesWindow.document.title = `Slide  ${slide}`;
  }
  document.body.onclick = nextSlide;
</script>

Hãy tưởng tượng chúng ta đóng cửa sổ trình duyệt do showNotes() tạo ở trên. Không có trình xử lý sự kiện nào lắng nghe để phát hiện rằng cửa sổ đã bị đóng. Vì vậy, mã của chúng ta không cần phải xoá mọi thông tin tham chiếu đến tài liệu. Hàm nextSlide() vẫn "hoạt động" vì được liên kết như một trình xử lý lượt nhấp trong trang chính của chúng ta, và việc nextSlide chứa tham chiếu đến notesWindow có nghĩa là cửa sổ vẫn được tham chiếu và không thể thu gom rác.

Hình minh hoạ cách các tham chiếu đến một cửa sổ ngăn không cho cửa sổ đó thu thập rác sau khi đóng.

Có một số trường hợp khác mà các tệp tham chiếu vô tình bị giữ lại khiến các cửa sổ tách riêng không đủ điều kiện để thu thập rác:

  • Bạn có thể đăng ký trình xử lý sự kiện trên tài liệu ban đầu của iframe trước khi khung di chuyển đến URL dự định. Điều này dẫn đến việc vô tình tham chiếu đến tài liệu và iframe vẫn tồn tại sau khi các tệp tham chiếu khác được dọn dẹp.

  • Một tài liệu nặng về bộ nhớ được tải trong một cửa sổ hoặc iframe có thể vô tình được lưu giữ trong bộ nhớ trong thời gian dài sau khi điều hướng đến một URL mới. Việc này thường là do trang mẹ giữ lại các tham chiếu đến tài liệu để cho phép xoá trình nghe.

  • Khi chuyển một đối tượng JavaScript vào một cửa sổ hoặc iframe khác, chuỗi nguyên mẫu của Đối tượng đó sẽ bao gồm các thông tin tham chiếu đến môi trường mà đối tượng đó được tạo, bao gồm cả cửa sổ đã tạo đối tượng đó. Điều này có nghĩa là bạn cần tránh lưu giữ tham chiếu đến đối tượng từ các cửa sổ khác vì tránh lưu giữ tham chiếu đến chính các cửa sổ đó.

    index.html:

    <script>
      let currentFiles;
      function load(files) {
        // this retains the popup:
        currentFiles = files;
      }
      window.open('upload.html');
    </script>
    

    upload.html:

    <input type="file" id="file" />
    <script>
      file.onchange = () => {
        parent.load(file.files);
      };
    </script>
    

Phát hiện rò rỉ bộ nhớ do các cửa sổ bị tách ra

Việc theo dõi rò rỉ bộ nhớ có thể rất khó khăn. Thường rất khó để tạo các bản sao riêng biệt của những vấn đề này, đặc biệt là khi liên quan đến nhiều tài liệu hoặc cửa sổ. Để mọi thứ trở nên phức tạp hơn, việc kiểm tra các tệp tham chiếu có khả năng bị rò rỉ có thể tạo ra các tham chiếu bổ sung ngăn chặn việc thu gom rác cho các đối tượng được kiểm tra. Để đạt được mục tiêu đó, bạn nên bắt đầu với những công cụ đặc biệt tránh tạo ra khả năng này.

Bạn nên chụp ảnh nhanh vùng nhớ khối xếp để bắt đầu gỡ lỗi các vấn đề về bộ nhớ. Nhờ vậy, bạn có thể xem được tại một thời điểm trong bộ nhớ mà ứng dụng hiện đang sử dụng – tất cả các đối tượng đã được tạo nhưng chưa thu gom rác. Ảnh chụp nhanh của vùng nhớ khối xếp chứa thông tin hữu ích về các đối tượng, bao gồm kích thước cũng như danh sách các biến và phương thức đóng tham chiếu đến các đối tượng đó.

Ảnh chụp màn hình ảnh chụp nhanh của vùng nhớ khối xếp trong Công cụ của Chrome cho nhà phát triển cho thấy các tệp tham chiếu giữ lại một đối tượng lớn.
Ảnh chụp nhanh của vùng nhớ khối xếp cho thấy các tệp tham chiếu giữ lại một đối tượng lớn.

Để ghi lại ảnh chụp nhanh của vùng nhớ khối xếp, hãy chuyển đến thẻ Bộ nhớ trong Công cụ của Chrome cho nhà phát triển rồi chọn Ảnh chụp nhanh của vùng nhớ khối xếp trong danh sách các loại lập hồ sơ có sẵn. Sau khi ghi xong, khung hiển thị Summary (Tóm tắt) sẽ hiển thị các đối tượng hiện tại trong bộ nhớ, được nhóm theo hàm khởi tạo.

Bản minh hoạ cách chụp nhanh vùng nhớ khối xếp trong Công cụ của Chrome cho nhà phát triển.

Phân tích tệp báo lỗi có thể là một công việc khó khăn và cũng khá khó khăn để tìm thấy thông tin phù hợp trong quá trình gỡ lỗi. Để giúp giải quyết vấn đề này, các kỹ sư của Chromium yossik@peledni@ đã phát triển một công cụ Heap Làm sạch độc lập có thể giúp làm nổi bật một nút cụ thể như cửa sổ rời. Khi chạy bộ nhớ khối xếp trên một dấu vết, bạn sẽ xoá các thông tin không cần thiết khác khỏi biểu đồ tỷ lệ giữ chân, giúp dấu vết trở nên sạch đẹp và dễ đọc hơn nhiều.

Đo lường bộ nhớ theo phương thức lập trình

Ảnh chụp nhanh của vùng nhớ khối xếp có mức độ chi tiết cao và là lựa chọn lý tưởng để xác định vị trí xảy ra rò rỉ, nhưng việc chụp nhanh vùng nhớ khối xếp là một quá trình thủ công. Một cách khác để kiểm tra xem có bị rò rỉ bộ nhớ hay không là lấy kích thước vùng nhớ khối xếp JavaScript hiện đang sử dụng qua API performance.memory:

Ảnh chụp màn hình một phần giao diện người dùng của Công cụ của Chrome cho nhà phát triển.
Kiểm tra kích thước vùng nhớ khối xếp JS đã sử dụng trong Công cụ cho nhà phát triển khi cửa sổ bật lên được tạo, đóng và không được tham chiếu.

API performance.memory chỉ cung cấp thông tin về kích thước vùng nhớ khối xếp JavaScript, nghĩa là API này không bao gồm bộ nhớ mà tài liệu và tài nguyên của cửa sổ bật lên sử dụng. Để có thông tin đầy đủ, chúng tôi cần sử dụng API performance.measureUserAgentSpecificMemory() mới hiện đang được dùng thử trong Chrome.

Giải pháp để tránh rò rỉ cửa sổ tách rời

Có 2 trường hợp phổ biến nhất mà cửa sổ tách ra gây rò rỉ bộ nhớ là khi tài liệu gốc giữ lại thông tin tham chiếu đến một cửa sổ bật lên đã đóng hoặc bị xoá iframe, và khi việc điều hướng ngoài ý muốn của một cửa sổ hoặc iframe dẫn đến việc trình xử lý sự kiện không bao giờ bị huỷ đăng ký.

Ví dụ: Đóng cửa sổ bật lên

Trong ví dụ sau, hai nút được dùng để mở và đóng cửa sổ bật lên. Để nút Close Popup (Đóng cửa sổ bật lên) hoạt động, thông tin tham chiếu đến cửa sổ bật lên đã mở sẽ được lưu trữ trong một biến:

<button id="open">Open Popup</button>
<button id="close">Close Popup</button>
<script>
  let popup;
  open.onclick = () => {
    popup = window.open('/login.html');
  };
  close.onclick = () => {
    popup.close();
  };
</script>

Thoạt nhìn, có vẻ như mã ở trên tránh được các lỗi phổ biến: không giữ lại thông tin tham chiếu đến tài liệu của cửa sổ bật lên và không có trình xử lý sự kiện nào được đăng ký trên cửa sổ bật lên. Tuy nhiên, sau khi người dùng nhấp vào nút Open Popup (Mở cửa sổ bật lên), biến popup sẽ tham chiếu đến cửa sổ đã mở và bạn có thể truy cập biến đó từ phạm vi của trình xử lý lượt nhấp nút Close Popup (Đóng cửa sổ bật lên). Trừ phi popup được chỉ định lại hoặc trình xử lý lượt nhấp bị xoá, tham chiếu kèm theo của trình xử lý đó đến popup có nghĩa là trình xử lý không thể thu thập rác.

Giải pháp: Huỷ đặt tệp tham chiếu

Các biến tham chiếu đến một cửa sổ khác hoặc tài liệu của cửa sổ đó khiến cửa sổ đó được lưu giữ trong bộ nhớ. Vì các đối tượng trong JavaScript luôn là tệp tham chiếu, nên việc gán giá trị mới cho các biến sẽ xoá mã tham chiếu đến đối tượng ban đầu. Để "huỷ đặt" tham chiếu đến một đối tượng, chúng ta có thể chỉ định lại các biến đó cho giá trị null.

Áp dụng điều này cho ví dụ về cửa sổ bật lên trước đó, chúng ta có thể sửa đổi trình xử lý nút đóng để "huỷ đặt" tham chiếu đến cửa sổ bật lên:

let popup;
open.onclick = () => {
  popup = window.open('/login.html');
};
close.onclick = () => {
  popup.close();
  popup = null;
};

Điều này có thể hữu ích, nhưng bộc lộ một vấn đề khác cụ thể đối với các cửa sổ được tạo bằng open(): điều gì sẽ xảy ra nếu người dùng đóng cửa sổ thay vì nhấp vào nút đóng tuỳ chỉnh? Ngoài ra, điều gì sẽ xảy ra nếu người dùng bắt đầu duyệt đến các trang web khác trong cửa sổ chúng ta đã mở? Mặc dù ban đầu có vẻ như đủ để huỷ thiết lập tham chiếu popup khi nhấp vào nút đóng, nhưng vẫn có tình trạng rò rỉ bộ nhớ khi người dùng không dùng nút cụ thể đó để đóng cửa sổ. Để giải quyết vấn đề này, bạn cần phát hiện các trường hợp này để huỷ đặt các tệp tham chiếu còn lại khi chúng xảy ra.

Giải pháp: Theo dõi và thải bỏ

Trong nhiều trường hợp, JavaScript chịu trách nhiệm mở cửa sổ hoặc tạo khung không có quyền kiểm soát độc quyền đối với vòng đời của các khung đó. Người dùng có thể đóng cửa sổ bật lên hoặc việc điều hướng đến một tài liệu mới có thể khiến tài liệu trước đó nằm trong một cửa sổ hoặc khung bị tách ra. Trong cả hai trường hợp, trình duyệt sẽ kích hoạt sự kiện pagehide để cho biết tài liệu đang được huỷ tải.

Bạn có thể dùng sự kiện pagehide để phát hiện các cửa sổ đã đóng và di chuyển ra khỏi tài liệu hiện tại. Tuy nhiên, có một lưu ý quan trọng: tất cả các cửa sổ và iframe mới tạo đều chứa một tài liệu trống, sau đó điều hướng không đồng bộ đến URL đã cho nếu được cung cấp. Do đó, sự kiện pagehide ban đầu sẽ được kích hoạt ngay sau khi tạo cửa sổ hoặc khung, ngay trước khi tài liệu mục tiêu được tải. Vì mã dọn dẹp tệp đối chiếu cần chạy khi tài liệu target bị huỷ tải, nên chúng ta cần bỏ qua sự kiện pagehide đầu tiên này. Có một số kỹ thuật để thực hiện việc này, trong đó đơn giản nhất là bỏ qua các sự kiện ẩn trang bắt nguồn từ URL about:blank của tài liệu ban đầu. Đây là giao diện của nút đó trong ví dụ về cửa sổ bật lên:

let popup;
open.onclick = () => {
  popup = window.open('/login.html');

  // listen for the popup being closed/exited:
  popup.addEventListener('pagehide', () => {
    // ignore initial event fired on "about:blank":
    if (!popup.location.host) return;

    // remove our reference to the popup window:
    popup = null;
  });
};

Điều quan trọng cần lưu ý là kỹ thuật này chỉ hoạt động trên các cửa sổ và khung có cùng nguồn gốc hiệu quả với trang mẹ nơi mã của chúng ta đang chạy. Khi tải nội dung từ một nguồn gốc khác, cả location.host và sự kiện pagehide đều không sử dụng được vì lý do bảo mật. Thông thường, bạn nên tránh duy trì các thông tin tham chiếu đến các nguồn gốc khác, nhưng trong một số ít trường hợp bắt buộc, bạn có thể theo dõi các thuộc tính window.closed hoặc frame.isConnected. Khi các thuộc tính này thay đổi để cho biết một cửa sổ đã đóng hoặc iframe đã bị xoá, bạn không nên huỷ đặt mọi tham chiếu đến iframe đó.

let popup = window.open('https://example.com');
let timer = setInterval(() => {
  if (popup.closed) {
    popup = null;
    clearInterval(timer);
  }
}, 1000);

Giải pháp: Sử dụng weakref

Gần đây, JavaScript gần đây đã hỗ trợ một cách mới để tham chiếu các đối tượng cho phép thu gom rác, gọi là WeakRef. WeakRef được tạo cho một đối tượng không phải là tham chiếu trực tiếp mà là một đối tượng riêng biệt cung cấp phương thức .deref() đặc biệt trả về tệp tham chiếu đến đối tượng miễn là đối tượng đó chưa được thu thập rác. Với WeakRef, bạn có thể truy cập vào giá trị hiện tại của một cửa sổ hoặc tài liệu mà vẫn cho phép thu gom rác. Thay vì giữ lại một thông tin tham chiếu đến cửa sổ phải được huỷ đặt theo cách thủ công để phản hồi các sự kiện như pagehide hoặc các thuộc tính như window.closed, bạn sẽ nhận được quyền truy cập vào cửa sổ đó khi cần. Khi cửa sổ được đóng, cửa sổ có thể được thu thập rác, khiến phương thức .deref() bắt đầu trả về undefined.

<button id="open">Open Popup</button>
<button id="close">Close Popup</button>
<script>
  let popup;
  open.onclick = () => {
    popup = new WeakRef(window.open('/login.html'));
  };
  close.onclick = () => {
    const win = popup.deref();
    if (win) win.close();
  };
</script>

Một chi tiết thú vị cần xem xét khi sử dụng WeakRef để truy cập vào cửa sổ hoặc tài liệu là tệp tham chiếu thường vẫn có sẵn trong một khoảng thời gian ngắn sau khi đóng cửa sổ hoặc iframe bị xoá. Điều này là do WeakRef tiếp tục trả về một giá trị cho đến khi đối tượng liên kết của nó được thu thập rác. Việc này xảy ra không đồng bộ trong JavaScript và thường trong thời gian không hoạt động. Rất may, khi kiểm tra các cửa sổ đã tách rời trong bảng điều khiển Bộ nhớ của Chrome Công cụ, việc chụp nhanh vùng nhớ khối xếp thực sự kích hoạt tính năng thu thập rác và loại bỏ cửa sổ được tham chiếu yếu. Bạn cũng có thể kiểm tra để đảm bảo một đối tượng được tham chiếu qua WeakRef đã bị loại bỏ khỏi JavaScript, bằng cách phát hiện thời điểm deref() trả về undefined hoặc sử dụng FinalizationRegistry API mới:

let popup = new WeakRef(window.open('/login.html'));

// Polling deref():
let timer = setInterval(() => {
  if (popup.deref() === undefined) {
    console.log('popup was garbage-collected');
    clearInterval(timer);
  }
}, 20);

// FinalizationRegistry API:
let finalizers = new FinalizationRegistry(() => {
  console.log('popup was garbage-collected');
});
finalizers.register(popup.deref());

Giải pháp: Giao tiếp qua postMessage

Việc phát hiện thời điểm đóng cửa sổ hoặc điều hướng huỷ tải một tài liệu sẽ giúp chúng ta có thể xoá trình xử lý và huỷ đặt các tệp tham chiếu để các cửa sổ đã tách có thể được thu gom rác. Tuy nhiên, những thay đổi này là cách khắc phục cụ thể cho một vấn đề đôi khi là mối quan tâm cơ bản hơn: ghép nối trực tiếp giữa các trang.

Có một phương pháp thay thế toàn diện hơn để tránh các tham chiếu cũ giữa cửa sổ và tài liệu: thiết lập hoạt động phân tách bằng cách giới hạn hoạt động giao tiếp giữa nhiều tài liệu ở postMessage(). Hãy nhớ lại ví dụ về ghi chú ban đầu của người trình bày, các hàm như nextSlide() đã cập nhật trực tiếp cửa sổ ghi chú bằng cách tham chiếu và chỉnh sửa nội dung của cửa sổ đó. Thay vào đó, trang chính có thể chuyển thông tin cần thiết tới cửa sổ ghi chú một cách không đồng bộ và gián tiếp qua postMessage().

let updateNotes;
function showNotes() {
  // keep the popup reference in a closure to prevent outside references:
  let win = window.open('/presenter-view.html');
  win.addEventListener('pagehide', () => {
    if (!win || !win.location.host) return; // ignore initial "about:blank"
    win = null;
  });
  // other functions must interact with the popup through this API:
  updateNotes = (data) => {
    if (!win) return;
    win.postMessage(data, location.origin);
  };
  // listen for messages from the notes window:
  addEventListener('message', (event) => {
    if (event.source !== win) return;
    if (event.data[0] === 'nextSlide') nextSlide();
  });
}
let slide = 1;
function nextSlide() {
  slide += 1;
  // if the popup is open, tell it to update without referencing it:
  if (updateNotes) {
    updateNotes(['setSlide', slide]);
  }
}
document.body.onclick = nextSlide;

Mặc dù phương thức này vẫn yêu cầu các cửa sổ tham chiếu lẫn nhau, nhưng không giữ lại tệp tham chiếu đến tài liệu hiện tại từ một cửa sổ khác. Phương pháp truyền thông báo cũng khuyến khích các thiết kế mà tệp tham chiếu cửa sổ được giữ ở một nơi duy nhất, nghĩa là chỉ cần bỏ đặt một tham chiếu duy nhất khi cửa sổ bị đóng hoặc điều hướng đi. Trong ví dụ trên, chỉ showNotes() giữ lại tham chiếu đến cửa sổ ghi chú và sử dụng sự kiện pagehide để đảm bảo rằng tham chiếu đó đã được xoá.

Giải pháp: Tránh tham chiếu bằng noopener

Trong trường hợp cửa sổ bật lên được mở mà trang của bạn không cần giao tiếp hoặc kiểm soát, bạn có thể tránh được việc tham chiếu đến cửa sổ đó. Điều này đặc biệt hữu ích khi tạo cửa sổ hoặc iframe sẽ tải nội dung từ một trang web khác. Đối với những trường hợp này, window.open() chấp nhận một tuỳ chọn "noopener", hoạt động giống như thuộc tính rel="noopener" đối với các đường liên kết HTML:

window.open('https://example.com/share', null, 'noopener');

Tuỳ chọn "noopener" khiến window.open() trả về null, khiến bạn không thể vô tình lưu trữ một tệp tham chiếu đến cửa sổ bật lên. Thao tác này cũng ngăn cửa sổ bật lên tham chiếu đến cửa sổ mẹ, vì thuộc tính window.opener sẽ là null.

Ý kiến phản hồi

Hy vọng rằng một số đề xuất trong bài viết này có thể giúp tìm và khắc phục lỗi rò rỉ bộ nhớ. Nếu bạn có một kỹ thuật khác để gỡ lỗi các cửa sổ đã tách rời hoặc bài viết này đã giúp phát hiện các rò rỉ trong ứng dụng của mình, tôi rất muốn biết! Bạn có thể tìm thấy tôi trên Twitter @_developit.