2013-05-08 30 views
18
function(foo, cb) { 
    var bigObject = new BigObject(); 
    doFoo(foo, function(e) { 
    if (e.type === bigObject.type) { 
      cb(); 
      // bigObject = null; 
    } 
    }); 
} 

Ví dụ trên cho thấy đóng cửa rò rỉ bộ nhớ cổ điển, ngẫu nhiên (hoặc có thể không). Bộ thu gom rác V8 không thể xác định xem có an toàn để xóa bigObject vì nó đang được sử dụng trong chức năng gọi lại có thể được gọi nhiều lần không.Sự rò rỉ bộ nhớ và bộ nhớ gọi lại trong javascript

Một giải pháp là đặt bigObject thành null khi công việc trong chức năng gọi lại kết thúc. Nhưng nếu bạn đang sử dụng nhiều biến (hãy tưởng tượng có n biến như bigObject và tất cả chúng đều được sử dụng trong gọi lại) thì việc làm sạch này trở thành một vấn đề xấu.

Câu hỏi của tôi là: có cách nào khác để làm sạch các biến được sử dụng không?

EDIT Đây là ví dụ khác (thế giới thực): Vì vậy, tôi nhận được ứng dụng từ mongodb và so sánh nó với một số ứng dụng khác. Gọi lại từ mongodb sử dụng ứng dụng biến được xác định trong số đó gọi lại. Sau khi tôi nhận được kết quả từ mongodb tôi trả lại nó cũng như một cuộc gọi lại (vì nó là tất cả async và tôi không thể chỉ viết trở lại). Vì vậy, thực sự nó có thể xảy ra mà tôi tuyên truyền gọi lại tất cả các cách để nguồn ...

function compareApplications(application, condition, callback) { 

    var model = database.getModel('Application'); 
    model.find(condition, function (err, applicationFromMongo) { 
     var result = (applicationFromMongo.applicationID == application.applicationID) 
     callback(result)   
    } 
} 
+0

Hãy để tôi hỏi bạn điều này - tại sao điều này là một vấn đề ? Trình xử lý 'change' có nghĩa là được gọi nhiều lần. Vì vậy, làm thế nào bạn sẽ (hoặc GC) bao giờ biết khi nó thực sự là kết thúc của việc sử dụng 'bigObject' trừ khi bạn unbind sự kiện' thay đổi'? Bạn dường như muốn một thể hiện của 'bigObject' để trình xử lý có thể so sánh các loại. Bạn khởi tạo nó một lần, làm giảm tải cho mỗi lần trình xử lý chạy. Nếu bạn muốn nó được dọn dẹp, hãy khởi tạo nó bên trong trình xử lý mỗi lần, hoặc mong đợi nó bị "rò rỉ" bộ nhớ vì đó là cách nó hoạt động. – Ian

+0

Cách sử dụng .one() thay vì .on()? – frenchie

+0

Xin lưu ý rằng tôi đã thay đổi ví dụ. Trong chương trình thế giới thực của tôi, tôi không sử dụng .on. Tôi vượt qua chức năng gọi lại khác. –

Trả lời

1

Nếu chức năng gọi lại của bạn chỉ được gọi là một lần, sau đó bạn nên hủy đăng ký sau khi nó được gọi. Điều đó sẽ phát hành gọi lại + đóng cửa của bạn đến GC. Với việc đóng cửa của bạn được phát hành, bigObject cũng sẽ được thu thập miễn phí bởi GC.

Đó là giải pháp tốt nhất - như bạn đã lưu ý, GC không kỳ diệu biết cuộc gọi lại của bạn sẽ chỉ được gọi một lần.

+2

Cảm ơn bạn đã trả lời nhưng bạn có thể đưa ra một số ví dụ về hủy đăng ký khỏi cuộc gọi lại không? –

+1

Tùy thuộc vào cơ chế bạn đang sử dụng để đăng ký gọi lại. Bạn không cung cấp đủ chi tiết về những gì 'doFoo()' làm. Hầu hết các khung công tác hoặc cung cấp một phương thức 'undoFoo()', hoặc 'doFoo()' tự trả về một phương thức mà bạn có thể gọi để hủy đăng ký. Nếu bạn đang sử dụng jQuery, bạn sẽ đăng ký với '$ (" ... "). Trên (" thay đổi ", cb);' và hủy đăng ký với '$ (" ... "). Off (" thay đổi ", cb) '. Trong thực tế, họ có một phương pháp cho tình huống chính xác mà bạn chỉ muốn đăng ký 1 sự kiện: '$ (" ... "). Một (" thay đổi ", cb)' sẽ tự động hủy đăng ký sau một cuộc gọi duy nhất. – Brandon

+1

Xin lưu ý rằng tôi đã thay đổi ví dụ trong bài đăng đầu tiên. Điều này bây giờ giống như vấn đề thực tế của tôi. Tôi chuyển chức năng gọi lại đến một hàm khác. Tôi sử dụng node.js. Foo chỉ là một ví dụ nhưng vấn đề cũng giống như trong các chức năng thế giới thực của tôi. Chức năng gọi lại đang sử dụng các biến trong phạm vi và GC không thể làm sạch nó một cách ngầm định: (... bộ nhớ rss của tôi tăng lên rất nhanh vì tôi có rất nhiều lưu lượng. –

0

Để xây dựng dựa trên câu trả lời của Brandon: Nếu (vì lý do khủng khiếp), bạn không thể bỏ đăng ký callback của bạn, bạn luôn có thể xử lý xóa callback mình:

function createSingleUseCallback(callback) 
{ 
    function callbackWrapper() 
    { 
     var ret = callback.apply(this, arguments); 
     delete callback; 
     return ret; 
    } 
    return callbackWrapper; 
} 

function compareApplications(application, condition, callback) 
{ 
    var model = database.getModel('Application'); 
    model.find(condition, createSingleUseCallback(function (err, applicationFromMongo) 
    { 
     var result = (applicationFromMongo.applicationID == application.applicationID); 
     callback(result); 
    }) 
}