2013-06-04 43 views
6

Hãy xem xét ví dụ này của phương pháp khá tiêu chuẩn trong góc Js, cập nhật quan điểm:Mẫu AJAX này có bị rò rỉ bộ nhớ không?

$scope.fetchResults = function() { 
    // Some local variable that will cause creation of closure 
    var hugeData = serviceX.getMilionRecords(); 

    // Any call to any resource with success and error handlers. 
    $http({ 
     method: "GET", 
     url: "/rest-api/bulk-operation-x", 
     params: { someParam: hugeData.length } 

    }).success(function() { 
     var length = hugeData.length; 
     $scope.reportToUser("Success, that was " + length + " records being processed!"; 

    }).error(function() { 
     var length = hugeData.length; 
     $scope.reportToUser("Something went wrong while processing " + length + " records... :-("; 
    }); 
}; 

Đây là khóa học ví dụ giả, nhưng nó độc đáo cho thấy mô hình, có thể được mô tả như tái sử dụng các biến địa phương từ bên trong Gọi lại AJAX.

Tất nhiên trong cả hai trình xử lý (successerror) chúng tôi đang tạo một đóng trên hugeData được tham chiếu trực tiếp từ trình xử lý gọi lại.

Câu hỏi của tôi là: vì kết quả của cuộc gọi AJAX chỉ có thể thành công hay thất bại, việc tái sử dụng mã này có gây ra rò rỉ bộ nhớ theo thời gian không? Tôi sẽ trả lời "có", nhưng tôi không thể chứng minh một cách đáng tin cậy điều này trong các bài kiểm tra địa phương của tôi.

Tôi muốn một số chuyên gia có kinh nghiệm hơn để giải thích vấn đề này cho tôi. Tôi rất thích phản ứng của bất cứ ai làm việc với Angular trên cơ sở hàng ngày, nhưng bất kỳ phản ứng jquery cũng được chào đón.

+2

"Bộ nhớ bị rò rỉ" là một thuật ngữ rất cụ thể đề cập đến bộ nhớ được cấp phát và sau đó không bao giờ được giải phóng. Nó chỉ áp dụng cho các bối cảnh nơi quản lý bộ nhớ được thực hiện thủ công. Cho rằng trong quản lý JS là minh bạch đối với lập trình viên, rò rỉ bộ nhớ chỉ có liên quan nếu nói về các mẫu mã hóa gây ra một lỗi thực hiện để rò rỉ bộ nhớ trong một số trường hợp, như trường hợp với các phiên bản cũ của IE. Tôi không chắc liệu câu hỏi có ý nghĩa như được viết hay không. – Jon

+0

Tôi không đồng ý với nhận xét của @ Jon. Một hành động xấu có thể gây rò rỉ bộ nhớ, như làm đầy các đối tượng trong phạm vi toàn cục, v.v. Trong trường hợp này nó sẽ không, vì biến bigData bị xóa ngay sau khi bất kỳ cuộc gọi lại nào được hoàn thành (và phiên bản http http được làm sạch lên) – rewritten

+0

@Jon, bạn có thể dễ dàng tạo ra một rò rỉ bộ nhớ trong bất kỳ ngôn ngữ nào với quản lý bộ nhớ trong suốt. Java, Scala, JavaScript, C#, bạn đặt tên cho nó. @rewritten, đây chính xác là những gì tôi đang nói đến. Làm thế nào để bạn biết rằng 'bigData' bị xóa khi' success() 'kết thúc? Từ quan điểm ngôn ngữ, việc đóng cửa được sử dụng trong 'error()' có thể được thực hiện tại một số thời điểm sau này trong tương lai. Trừ khi Angular thực hiện điều gì đó dưới mui xe (như vô hiệu hóa các cuộc gọi lại khác khi kết thúc), thì chúng ta có thể bị rò rỉ bộ nhớ ở đây. –

Trả lời

4

Bạn sẽ bị rò rỉ bộ nhớ ngay khi bạn trả lại kết quả cuộc gọi $http() (hoặc bất kỳ đối tượng hoặc chức năng nào có quyền truy cập hugeData) vào phạm vi ngoài của fetchResults.

Với mã của bạn, không có gì lớn được hiển thị trực tiếp bên ngoài fetchResults và kết quả cuộc gọi $http() sẽ hoạt động cho đến khi thành công hoặc không thành công, sau đó gọi lại cuộc gọi tương ứng, cuối cùng nhận được GC'ed.

Xem cho những hiểu biết: http://jibbering.com/faq/notes/closures/#clIdRes

Như @ ŁukaszBachman quan sát, điều này không đảm bảo rằng không có rò rỉ bộ nhớ. Bất kỳ tham chiếu lơ lửng nào đối với đối tượng lớn của bạn hoặc để gọi lại của bạn với đối tượng lớn trong phạm vi, sẽ gây ra sự khốn khổ.

Vì vậy, hãy kiểm tra triển khai $q ($http được dựa trên $q).

Nếu bạn kiểm tra https://github.com/angular/angular.js/blob/master/src/ng/q.js#L191, bạn có thể thấy rằng resolve() phương pháp của các bản sao đầu tiên hoãn lại danh sách các callbacks đăng ký tại một biến địa phương để phương pháp:

var callbacks = pending; 

sau đó làm vô hiệu bên ngoài pending (mà đã được xác định ở cấp độ defer)

pending = undefined; 

sau đó, đánh dấu tiếp theo, thực hiện cuộc gọi lại. Mọi thứ có thể trở nên phức tạp bởi thực tế là đối số của cuộc gọi lại có thể bị trì hoãn (thêm một sự trì hoãn nữa để thực hiện), nhưng nhiều nhất bạn có thể đi vào vòng lặp vô hạn. (Và đó không phải là buồn cười!). Nếu bạn đủ may mắn không nhận được vào vòng lặp, sau đó tại một số điểm mảng gọi lại là kiệt sức, và sau đó không có tài liệu tham khảo nào cho danh sách gọi lại, do đó, nó có sẵn cho GC.

Nhưng.

Mọi thứ có thể xảy ra nếu bạn buộc họ.

Bạn có thể sử dụng arguments.callee bên trong một cuộc gọi lại.

Bạn cũng có thể ném bia trên bàn phím của mình.

Nếu bạn nhảy ra ngoài cửa sổ, trừ khi bạn sống ở tầng một, có thể bạn sẽ bị thương.

Chúc mừng EcmaScripting!

+0

Tại sao bạn chắc chắn về 'kết quả của cuộc gọi $ http() sẽ tồn tại cho đến khi nó thành công hoặc thất bại'? AFAIU đóng cửa được quản lý động cơ JS của tôi, vì vậy từ ví dụ của tôi bạn không thể chắc chắn, phải không? Bạn sẽ phải kiểm tra impl bên trong của '$ http' để xác minh rằng callback thứ hai được xử lý và do đó có thể được giải phóng bởi GC. Tôi có đúng không? (Tôi đã thêm bài viết được cung cấp vào danh sách đọc của mình) –

+0

Kiểm tra triển khai '$ q', đặc biệt là phương thức' resolve' (https://github.com/angular/angular.js/blob/master/src/ng/ q.js # L191). Chuỗi gọi lại 'pending' được loại bỏ hoàn toàn khỏi phạm vi lời hứa ngay sau khi lời hứa được giải quyết, và nó được chuyển thành một biến khác trong phương thức phân giải. Khi giải quyết hoàn tất, không còn tham chiếu nào đến các cuộc gọi lại, làm cho chúng có sẵn cho GC. – rewritten

+0

Nói cách khác, bạn là chính xác. Và nó được thực hiện chính xác theo cách đó, danh sách đầy đủ các cuộc gọi lại đã đăng ký được làm sạch trong quá trình giải quyết. – rewritten

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