Bộ sưu tập rác JavaScript là gì?


Câu trả lời:


192

Eric Lippert đã viết một bài đăng trên blog chi tiết về chủ đề này một thời gian trước (ngoài ra so sánh nó với VBScript ). Chính xác hơn, ông đã viết về JScript , đây là triển khai ECMAScript của riêng Microsoft, mặc dù rất giống với JavaScript. Tôi sẽ tưởng tượng rằng bạn có thể cho rằng phần lớn hành vi sẽ giống với công cụ JavaScript của Internet Explorer. Tất nhiên, việc triển khai sẽ thay đổi tùy theo trình duyệt, mặc dù tôi nghi ngờ bạn có thể áp dụng một số nguyên tắc chung và áp dụng chúng cho các trình duyệt khác.

Trích dẫn từ trang đó:

JScript sử dụng trình thu gom rác đánh dấu và quét không cần thiết. Nó hoạt động như thế này:

  • Mỗi biến "trong phạm vi" được gọi là "scavenger". Một người nhặt rác có thể đề cập đến một số, một đối tượng, một chuỗi, bất cứ điều gì. Chúng tôi duy trì một danh sách những người nhặt rác - các biến được chuyển sang danh sách scav khi chúng đi vào phạm vi và ngoài danh sách scav khi chúng đi ra khỏi phạm vi.

  • Thỉnh thoảng người thu gom rác chạy. Đầu tiên, nó đặt một "dấu" trên mọi đối tượng, biến, chuỗi, v.v. - tất cả bộ nhớ được theo dõi bởi GC. (JScript sử dụng cấu trúc dữ liệu VariANT bên trong và có rất nhiều bit không được sử dụng thêm trong cấu trúc đó, vì vậy chúng tôi chỉ đặt một trong số chúng.)

  • Thứ hai, nó xóa dấu hiệu trên các scavengers và đóng cửa tạm thời của các tài liệu tham khảo scavenger. Vì vậy, nếu một đối tượng scavenger tham chiếu đến một đối tượng nonscavenger thì chúng ta sẽ xóa các bit trên nonscavenger và trên tất cả mọi thứ mà nó đề cập đến. (Tôi đang sử dụng từ "đóng cửa" theo nghĩa khác với trong bài viết trước đây của tôi.)

  • Tại thời điểm này, chúng ta biết rằng tất cả bộ nhớ vẫn được đánh dấu là bộ nhớ được phân bổ không thể đạt được bởi bất kỳ đường dẫn nào từ bất kỳ biến trong phạm vi nào. Tất cả những đối tượng đó được hướng dẫn tự xé xuống, phá hủy mọi tham chiếu vòng tròn.

Mục đích chính của thu gom rác là cho phép lập trình viên không phải lo lắng về việc quản lý bộ nhớ của các đối tượng họ tạo và sử dụng, mặc dù đôi khi không tránh khỏi điều đó - đôi khi luôn có một ý tưởng sơ bộ về cách thức hoạt động của bộ sưu tập rác .

Ghi chú lịch sử: bản sửa đổi trước đó của câu trả lời có tham chiếu không chính xác đến deletetoán tử. Trong JavaScript , deletetoán tử loại bỏ một thuộc tính khỏi một đối tượng và hoàn toàn khác với deleteC / C ++.


27
hướng dẫn của Apple còn thiếu sót: trình tự động sử dụng deletekhông chính xác; ví dụ, trong ví dụ đầu tiên, thay vì delete foo, trước tiên bạn nên loại bỏ trình nghe sự kiện thông qua window.removeEventListener()và sau đó sử dụng foo = nullđể ghi đè lên biến; trong IE, delete window.foo(nhưng không delete foo) cũng sẽ hoạt động nếu foolà toàn cầu, nhưng ngay cả khi đó nó sẽ không ở FF hoặc Opera
Christoph

3
Xin lưu ý rằng bài viết của Eric nên được xem là "chỉ dành cho mục đích lịch sử". Nhưng nó vẫn có nhiều thông tin.
Peter Ivan

2
Cũng lưu ý - IE 6 và 7 KHÔNG sử dụng trình thu gom rác quét và đánh dấu không phát sinh. Họ sử dụng một trình thu gom rác tham chiếu đơn giản, dễ bị ảnh hưởng bởi các vấn đề tham chiếu vòng tròn với việc thu gom rác.
Doug

1
ECMAScript deletelà một toán tử đơn nguyên (một biểu thức), không phải là một câu lệnh (tức là delete 0, delete 0, delete 3:). Nó trông giống như tuyên bố khi được thể hiện bằng một tuyên bố biểu thức.
Hydroper

Yeh câu trả lời tại thời điểm đó đã lỗi thời, kể từ năm 2012, các trình duyệt hiện đại sử dụng một dấu hiệu / quét đại số .. vì vậy nó không phụ thuộc phạm vi nữa. Tham khảo: developer.mozilla.org/en-US/docs/Web/JavaScript/
Kẻ

52

Cảnh giác với các tham chiếu vòng tròn khi các đối tượng DOM có liên quan:

Các mẫu rò rỉ bộ nhớ trong JavaScript

Hãy nhớ rằng bộ nhớ chỉ có thể được thu hồi khi không có tham chiếu hoạt động đến đối tượng. Đây là một cạm bẫy phổ biến với các bao đóng và xử lý sự kiện, vì một số công cụ JS sẽ không kiểm tra biến nào thực sự được tham chiếu trong các hàm bên trong và chỉ giữ tất cả các biến cục bộ của các hàm kèm theo.

Đây là một ví dụ đơn giản:

function init() {
    var bigString = new Array(1000).join('xxx');
    var foo = document.getElementById('foo');
    foo.onclick = function() {
        // this might create a closure over `bigString`,
        // even if `bigString` isn't referenced anywhere!
    };
}

Một triển khai JS ngây thơ không thể thu thập bigStringmiễn là xung quanh xử lý sự kiện. Có một số cách để giải quyết vấn đề này, ví dụ: cài đặt bigString = nullở cuối init()( deletesẽ không hoạt động đối với các biến cục bộ và đối số hàm: deletexóa các thuộc tính khỏi các đối tượng và đối tượng biến không thể truy cập - ES5 ở chế độ nghiêm ngặt thậm chí sẽ ném ReferenceErrornếu bạn thử để xóa một biến cục bộ!).

Tôi khuyên bạn nên tránh đóng cửa không cần thiết càng nhiều càng tốt nếu bạn quan tâm đến việc tiêu thụ bộ nhớ.


20
Lỗi tham chiếu vòng tròn DOM là dành riêng cho JScript - không có trình duyệt nào khác bị lỗi này ngoài IE. Trên thực tế, tôi khá chắc chắn rằng thông số ECMAScript tuyên bố rõ ràng rằng GC phải có khả năng xử lý các chu kỳ như vậy: - /
olliej

@olliej: Tôi không thấy bất kỳ đề cập nào về GC trong thông số ECMAScript .
Janus Troelsen


16

Trích dẫn tốt từ một blog

Thành phần DOM là "rác được thu thập", cũng như thành phần JScript, có nghĩa là nếu bạn tạo một đối tượng trong một trong hai thành phần, và sau đó mất dấu vết của đối tượng đó, cuối cùng nó sẽ bị xóa.

Ví dụ:

function makeABigObject() {
var bigArray = new Array(20000);
}

Khi bạn gọi hàm đó, thành phần JScript sẽ tạo một đối tượng (được đặt tên là bigArray) có thể truy cập được trong hàm. Tuy nhiên, ngay sau khi hàm trả về, bạn "mất dấu" của bigArray vì không còn cách nào để tham khảo nó nữa. Chà, thành phần JScript nhận ra rằng bạn đã mất dấu vết của nó và vì vậy bigArray đã được dọn sạch - bộ nhớ của nó được lấy lại. Loại tương tự hoạt động trong thành phần DOM. Nếu bạn nói document.createElement('div'), hoặc một cái gì đó tương tự, thì thành phần DOM tạo một đối tượng cho bạn. Khi bạn mất dấu vết của đối tượng đó bằng cách nào đó, thành phần DOM sẽ dọn sạch các liên quan.


13

Theo hiểu biết tốt nhất của tôi, các đối tượng của JavaScript là rác được thu thập định kỳ khi không còn tài liệu tham khảo nào cho đối tượng. Đó là điều xảy ra tự động, nhưng nếu bạn muốn xem thêm về cách thức hoạt động của nó, ở cấp độ C ++, hãy xem mã nguồn WebKit hoặc V8

Thông thường, bạn không cần phải suy nghĩ về nó, tuy nhiên, trong các trình duyệt cũ hơn, như IE 5.5 và các phiên bản đầu tiên của IE 6, và có lẽ các phiên bản hiện tại, việc đóng cửa sẽ tạo ra các tham chiếu vòng tròn mà khi không được kiểm tra sẽ làm hết bộ nhớ. Trong trường hợp cụ thể mà tôi muốn nói về việc đóng cửa, đó là khi bạn thêm một tham chiếu JavaScript vào một đối tượng dom và một đối tượng cho một đối tượng DOM được gọi lại cho đối tượng JavaScript. Về cơ bản, nó không bao giờ có thể được thu thập và cuối cùng sẽ khiến HĐH trở nên không ổn định trong các ứng dụng thử nghiệm được lặp để tạo sự cố. Trong thực tế, các rò rỉ này thường nhỏ, nhưng để giữ cho mã của bạn sạch sẽ, bạn nên xóa tham chiếu JavaScript đến đối tượng DOM.

Thông thường nên sử dụng từ khóa xóa để ngay lập tức hủy tham chiếu các đối tượng lớn như dữ liệu JSON mà bạn đã nhận lại và thực hiện bất cứ điều gì bạn cần làm với nó, đặc biệt là trong phát triển web di động. Điều này gây ra lần quét tiếp theo của GC để loại bỏ đối tượng đó và giải phóng bộ nhớ của nó.


Có phải vấn đề tham chiếu vòng tròn JavaScript -> DOM -> JavaScript đã được giải quyết trong các phiên bản IE mới hơn không? Nếu vậy, từ khi nào? Tôi nghĩ rằng nó là kiến ​​trúc rất sâu và không bao giờ được sửa chữa. Bạn có nguồn nào không?
erikkallen

Chỉ là giai thoại. Tôi đã không nhận thấy các rò rỉ điên rồ trong IE 8 chạy ở chế độ tiêu chuẩn, không phải chế độ bị hỏng. Tôi sẽ điều chỉnh phản ứng của mình.
Heat Miser

1
@erikkallen: vâng, lỗi GC đã được sửa trong các phiên bản IE 8+, vì các phiên bản cũ hơn đang sử dụng thuật toán thu gom rác rất ngây thơ, khiến cho một cặp đối tượng không thể liên kết với nhau. Các mark-and-sweepthuật toán phong cách mới hơn đảm nhận việc này .
kumarharsh

6

thu gom rác (GC) là một hình thức quản lý bộ nhớ tự động bằng cách loại bỏ các đối tượng không cần thiết nữa.

mọi quá trình xử lý bộ nhớ đều tuân theo các bước sau:

1 - phân bổ dung lượng bộ nhớ bạn cần

2 - làm một số xử lý

3 - giải phóng không gian bộ nhớ này

Có hai thuật toán chính được sử dụng để phát hiện đối tượng nào không cần thiết nữa.

Bộ sưu tập rác đếm tham chiếu : thuật toán này giảm định nghĩa "một đối tượng không còn cần thiết nữa" thành "một đối tượng không có đối tượng nào khác tham chiếu đến nó", đối tượng sẽ xóa nếu không có điểm tham chiếu đến nó

Thuật toán đánh dấu và quét : kết nối từng đối tượng với nguồn gốc. bất kỳ đối tượng nào không kết nối với root hoặc đối tượng khác. đối tượng này sẽ bị xóa.

hiện nay hầu hết các trình duyệt hiện đại sử dụng thuật toán thứ hai.


1
Và để thêm nguồn này, hãy xem MDN: developer.mozilla.org/en-US/docs/Web/JavaScript/iêu
Xenos

4

"Trong khoa học máy tính, bộ sưu tập rác (GC) là một hình thức quản lý bộ nhớ tự động. Trình thu gom rác, hoặc chỉ là trình thu gom, cố gắng lấy lại rác hoặc bộ nhớ được sử dụng bởi các đối tượng sẽ không bao giờ được truy cập hoặc biến đổi bởi ứng dụng."

Tất cả các công cụ JavaScript đều có trình thu gom rác riêng và chúng có thể khác nhau. Hầu hết thời gian bạn không phải đối phó với họ vì họ chỉ làm những gì họ phải làm.

Viết mã tốt hơn chủ yếu phụ thuộc vào mức độ bạn biết các nguyên tắc lập trình, ngôn ngữ và cách triển khai cụ thể.


1

Bộ sưu tập rác JavaScript là gì?

kiểm tra cái này

Điều gì quan trọng đối với một lập trình viên web để hiểu về bộ sưu tập rác JavaScript, để viết mã tốt hơn?

Trong Javascript, bạn không quan tâm đến việc cấp phát và phân bổ bộ nhớ. Toàn bộ vấn đề được yêu cầu cho trình thông dịch Javascript. Rò rỉ vẫn có thể có trong Javascript, nhưng chúng là lỗi của trình thông dịch. Nếu bạn quan tâm đến chủ đề này, bạn có thể đọc thêm trong www.memoryman Quản lý.org


Hệ thống quản lý bộ nhớ nào khác trong bài viết mà bạn liên kết là hệ thống được JavaScript sử dụng? "Rò rỉ vẫn có thể xảy ra trong Javascript, nhưng chúng là lỗi của trình thông dịch." - Điều đó không có nghĩa là các lập trình viên JS có thể đơn giản bỏ qua toàn bộ vấn đề, ví dụ, có một vấn đề tham chiếu vòng tròn khá nổi tiếng của JS <-> DOM trong các phiên bản IE cũ hơn mà bạn có thể giải quyết trong mã JS của mình. Ngoài ra, cách đóng cửa của JS là một tính năng thiết kế, không phải là lỗi, nhưng bạn có thể kết nối các khối bộ nhớ lớn hơn dự định nếu bạn sử dụng các bao đóng "không phù hợp" (Tôi không nói là không sử dụng chúng).
nnnnnn

3
Rò rỉ bộ nhớ là một con thú trong JavaScript. Nếu bạn đang viết một ứng dụng "dự án đại học" đơn giản, thì không phải lo lắng. Nhưng khi bạn bắt đầu viết các ứng dụng cấp doanh nghiệp hiệu suất cao, quản lý bộ nhớ trong JavaScript là điều bắt buộc.
Doug

1

Trên cửa sổ, bạn có thể sử dụng Drip.exe để tìm rò rỉ bộ nhớ hoặc kiểm tra xem thói quen mem miễn phí của bạn có hoạt động không.

Điều đó thực sự đơn giản, chỉ cần nhập URL trang web và bạn sẽ thấy mức tiêu thụ bộ nhớ của trình kết xuất IE tích hợp. Sau đó nhấn refresh, nếu bộ nhớ tăng, bạn đã tìm thấy rò rỉ bộ nhớ ở đâu đó trên trang web. Nhưng điều này cũng rất hữu ích để xem liệu các thói quen giải phóng bộ nhớ có hoạt động với IE không.


1

Các kiểu tham chiếu không lưu trữ đối tượng trực tiếp vào biến được gán, vì vậy biến đối tượng trong ví dụ này không thực sự chứa đối tượng. Thay vào đó, nó giữ một con trỏ (hoặc tham chiếu) đến vị trí trong bộ nhớ nơi đối tượng tồn tại

var object = new Object();

nếu bạn gán một biến cho một biến khác, mỗi biến sẽ có một bản sao của con trỏ và cả hai vẫn tham chiếu cùng một đối tượng trong bộ nhớ.

var object1 = new Object();
var object2 = object1;

Hai biến chỉ vào một đối tượng

JavaScript là ngôn ngữ được thu gom rác , vì vậy bạn không thực sự cần phải lo lắng về việc phân bổ bộ nhớ khi bạn sử dụng các loại tham chiếu. Tuy nhiên, cách tốt nhất để tới đích của các đối tượng mà bạn không còn cần để các nhà sưu tập rác có thể giải phóng bộ nhớ. Cách tốt nhất để làm điều này là đặt biến đối tượng thành null.

var object1 = new Object();
// do something
object1 = null; // dereference

Các đối tượng hội nghị đặc biệt quan trọng trong các ứng dụng rất lớn sử dụng hàng triệu đối tượng.

từ các nguyên tắc của JavaScript hướng đối tượng - NICHOLAS C. ZAKAS

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.