2015-09-30 13 views
6

Tôi đang đọc bài viết này (http://javascript.info/tutorial/memory-leaks#memory-leak-size) về rò rỉ bộ nhớ mà đề cập đến điều này như một rò rỉ bộ nhớ:Chúng tôi có cần phải dọn dẹp các biến không được trả lời theo cách thủ công trong quá trình đóng không?

function f() { 
    var data = "Large piece of data"; 

    function inner() { 
     return "Foo"; 
    } 

    return inner; 
} 

thông dịch JavaScript đã có ý tưởng mà các biến thể được yêu cầu bởi chức năng bên trong, vì vậy nó giữ mọi điều. Trong mọi trường hợp bên ngoài LexicalEnvironment. Tôi hy vọng, phiên dịch mới hơn sẽ cố gắng tối ưu hóa nó, nhưng không chắc chắn về thành công của họ.

Bài viết đề xuất chúng ta cần đặt thủ công data = null trước khi chúng tôi trả về hàm bên trong.

Điều này có đúng không? Hay bài viết này đã lỗi thời? (Nếu nó đã lỗi thời, ai đó có thể chỉ cho tôi tài nguyên về những cạm bẫy hiện tại)

Trả lời

5

Động cơ hiện đại sẽ không duy trì các biến không sử dụng trong phạm vi bên ngoài.

Do đó, bạn không đặt data = null trước khi trả về hàm bên trong, vì hàm bên trong không phụ thuộc vào ("đóng cửa") data.

Nếu chức năng bên trong đã phụ thuộc vào data --perhaps nó sẽ trả về nó - sau đó thiết lập data = null chắc chắn là không những gì bạn muốn, bởi vì sau đó, tốt, nó sẽ được null thay vì có giá trị ban đầu của nó !

Giả sử hàm bên trong phụ thuộc vào data, sau đó có, miễn là inner đang được trỏ đến (được gọi bởi) một cái gì đó, thì giá trị của data sẽ phải được giữ lại. Nhưng, đó là những gì bạn đang nói bạn muốn! Làm thế nào bạn có thể có một cái gì đó có sẵn mà không có nó có sẵn?

Hãy nhớ rằng tại một số thời điểm, biến giữ giá trị trả lại là f() sẽ tự thoát khỏi phạm vi. Tại thời điểm đó, ít nhất cho đến khi f() được gọi lại, data sẽ bị thu gom rác.

Quy tắc chung là bạn không cần phải lo lắng về bộ nhớ và rò rỉ với JavaScript. Đó là toàn bộ điểm của GC. Người thu gom rác thải làm một công việc tuyệt vời để xác định cái gì là cần thiết và cái gì không cần thiết, và giữ cái cũ và rác thu thập cái sau.

Bạn có thể muốn xem xét ví dụ sau:

function foo() { 
    var x = 1; 
    return function() { debugger; return 1; }; 
} 

function bar() { 
    var x = 1; 
    return function() { debugger; return x; }; 
} 

foo()(); 
bar()(); 

Và kiểm tra thực hiện của nó trong Chrome DevTools cửa sổ biến. Khi trình gỡ lỗi dừng ở hàm bên trong của foo, lưu ý rằng x không có mặt dưới dạng biến cục bộ hoặc dưới dạng đóng. Đối với tất cả các mục đích thực tế, nó không tồn tại.

Khi trình gỡ lỗi dừng trong chức năng bên trong của bar, chúng ta sẽ thấy biến số x, vì nó phải được giữ nguyên để có thể truy cập để được trả lại.

Điều này có đúng không? Hay bài viết này đã lỗi thời?

Không, không, và có, đúng vậy. Bài viết là bốn tuổi, là một đời trong thế giới web. Tôi không có cách nào để biết nếu jQuery vẫn còn bị rò rỉ, nhưng tôi sẽ ngạc nhiên nếu nó được, và nếu như vậy, có một cách dễ dàng, đủ để tránh chúng - không sử dụng jQuery. Việc rò rỉ tác giả của bài viết đề cập đến các vòng lặp DOM và trình xử lý sự kiện không có trong các trình duyệt hiện đại, theo đó tôi có nghĩa là IE10 (nhiều khả năng là IE9) trở lên. Tôi khuyên bạn nên tìm kiếm một tài liệu tham khảo cập nhật hơn nếu bạn thực sự muốn hiểu về rò rỉ bộ nhớ. Trên thực tế, tôi đề nghị bạn chủ yếu là ngừng lo lắng về rò rỉ bộ nhớ. Chúng chỉ xuất hiện trong những tình huống rất chuyên biệt. Thật khó để tìm thấy nhiều về chủ đề trên web những ngày này vì lý do chính xác đó. Dưới đây là một bài viết tôi đã tìm thấy: http://point.davidglasser.net/2013/06/27/surprising-javascript-memory-leak.html.

+0

Cảm ơn torazaburo. Điều làm tôi lo lắng là đoạn cuối cùng trong bài viết bạn đã cung cấp về lỗi Meteor thực sự làm rò rỉ bộ nhớ trong V8. – Jonathan

+0

"Trên thực tế, tôi đề nghị bạn chủ yếu ngừng lo lắng về rò rỉ bộ nhớ. Chúng chỉ xảy ra trong những tình huống rất chuyên biệt". Tôi mạnh mẽ không đồng ý với tuyên bố này. Ít nhất các DOM tách rời là một sự xuất hiện thường xuyên trong các SPA và mọi nhà phát triển đều phải biết về nó. –

1

Chỉ cần thêm vào câu trả lời tuyệt vời của @ torazaburo, điều đáng nói là các ví dụ trong hướng dẫn đó không bị rò rỉ. Một rò rỉ những gì xảy ra khi một chương trình xóa một tham chiếu đến một cái gì đó nhưng không giải phóng bộ nhớ nó tiêu thụ. Lần cuối cùng tôi nhớ rằng các nhà phát triển JS đã thực sự lo lắng về rò rỉ chính hãng là khi Internet Explorer (6 và 7 tôi nghĩ) sử dụng quản lý bộ nhớ riêng cho DOM và cho JS. Bởi vì điều này, nó có thể ràng buộc một sự kiện onclick vào một nút, phá hủy nút và vẫn có trình xử lý sự kiện bị mắc kẹt trong bộ nhớ - mãi mãi (hoặc cho đến khi trình duyệt bị lỗi hoặc đã bị đóng bởi người dùng). Bạn không thể kích hoạt trình xử lý hoặc giải phóng nó sau khi thực tế. Nó chỉ ngồi trên ngăn xếp, chiếm phòng. Vì vậy, nếu bạn đã có một ứng dụng web lâu đời hoặc một trang web đã tạo và phá hủy rất nhiều phần tử DOM, bạn phải siêu tinh tấn để luôn luôn unbind các sự kiện trước khi tiêu diệt chúng.

Tôi cũng đã gặp phải một số rò rỉ gây phiền nhiễu trong iOS nhưng đây là tất cả các lỗi và (cuối cùng) được Apple vá.

Điều đó nói rằng, một nhà phát triển giỏi cần lưu ý quản lý tài nguyên khi viết mã. Hãy xem xét hai cấu trúc này:

function F() { 
    var data = "One megabyte of data"; 

    this.inner = new function() { 
     return data; 
    } 
} 

var G = function() {}; 
G.prototype.data = "One megabyte of data"; 
G.prototype.inner = function() { 
    return this.data; 
}; 

Nếu bạn đã tạo ra một ngàn trường hợp của F, trình duyệt sẽ phải phân bổ một gigabyte thêm bộ nhớ cho tất cả những bản sao của chuỗi rất lớn. Và mỗi khi bạn xóa một thể hiện, bạn có thể nhận được một số jankiness trên màn hình khi GC cuối cùng phục hồi ram đó. Mặt khác, nếu bạn thực hiện một nghìn phiên bản của G, chuỗi lớn sẽ được tạo ra một lần và được tái sử dụng bởi mọi cá thể. Đó là một sự tăng hiệu suất rất lớn.

Nhưng lợi thế của F là chuỗi lớn về cơ bản là riêng tư. Không có mã nào khác ngoài hàm tạo sẽ có thể truy cập chuỗi đó trực tiếp. Do đó, mỗi trường hợp của F có thể thay đổi chuỗi đó nhiều như mong muốn và bạn sẽ không bao giờ phải lo lắng về việc gây ra sự cố cho các phiên bản khác.

Mặt khác, chuỗi lớn trong G được đặt ở đó để mọi người thay đổi. Các trường hợp khác có thể thay đổi nó và bất kỳ mã nào có cùng phạm vi với G cũng có thể.

Vì vậy, trong trường hợp này, có sự cân bằng giữa sử dụng tài nguyên và bảo mật.

+0

Tốt đề cập đến việc thêm dữ liệu được chia sẻ vào mẫu thử nghiệm.Tôi cũng sẽ thả một liên kết ở đây để [câu hỏi này về tham chiếu vòng tròn trong js] (http://stackoverflow.com/q/7347203/2407212) vì điều này cũng liên quan đến niềm tin phổ biến liên quan đến rò rỉ bộ nhớ. – Jonathan

Các vấn đề liên quan