2009-06-19 52 views
10

Tôi thích để xác định trình tự đệ quy như sau:Làm đệ quy đệ quy bộ nhớ rò rỉ?

let rec startFrom x = 
    seq { 
     yield x; 
     yield! startFrom (x + 1) 
    } 

Tôi không chắc chắn nếu chuỗi đệ quy như thế này nên được sử dụng trong thực tế. yield!xuất hiện là đuôi đệ quy, nhưng tôi không chắc chắn 100% kể từ khi được gọi từ bên trong một IEnumerable khác. Theo quan điểm của tôi, mã tạo ra một thể hiện của IEnumerable trên mỗi cuộc gọi mà không đóng nó, điều này thực sự sẽ làm cho chức năng này bị rò rỉ bộ nhớ.

Chức năng này có bị rò rỉ bộ nhớ không? Cho rằng vấn đề là nó thậm chí "đuôi đệ quy"?

[Chỉnh sửa để thêm]: Tôi đang dò dẫm xung quanh với NProf cho câu trả lời, nhưng tôi nghĩ sẽ rất hữu ích khi nhận được giải thích kỹ thuật về việc triển khai chuỗi đệ quy trên SO.

+2

>> "Thật không may, tôi không có kinh nghiệm sâu sắc, vì vậy tôi có thể 't tìm ra câu trả lời của riêng tôi. " Một số loại trò đùa? Làm sao người ta có được kinh nghiệm? – user79755

+2

Đối với hồ sơ tôi đang xem NProf ngay bây giờ, nhưng hy vọng sẽ nhận được câu trả lời nhanh hơn và giải thích kỹ thuật về SO. – Juliet

Trả lời

7

tôi tại nơi làm việc ngay bây giờ để tôi nhìn vào bit hơi mới hơn beta1, nhưng trên hộp của tôi trong chế độ Release và sau đó nhìn vào mã biên dịch với Net Reflector, dường như hai

let rec startFromA x =  
    seq {   
     yield x  
     yield! startFromA (x + 1)  
    } 

let startFromB x =  
    let z = ref x 
    seq {   
     while true do 
      yield !z 
      incr z 
    } 

tạo mã MSIL gần như giống hệt nhau khi được biên dịch ở chế độ 'Phát hành'. Và họ chạy vào khoảng tốc độ tương tự như mã này # C:

public class CSharpExample 
{ 
    public static IEnumerable<int> StartFrom(int x) 
    { 
     while (true) 
     { 
      yield return x; 
      x++; 
     } 
    } 
} 

(ví dụ tôi chạy tất cả ba phiên bản trên hộp của tôi và in kết quả phần triệu, và mỗi phiên bản mất khoảng 1.3s, +/- 1s). (Tôi không làm bất kỳ hồ sơ bộ nhớ nào; có thể tôi thiếu một số thứ quan trọng.)

Tóm lại, tôi sẽ không đổ mồ hôi quá nhiều về các vấn đề như thế này trừ khi bạn đo lường và xem vấn đề.

EDIT

tôi nhận ra tôi đã không thực sự trả lời câu hỏi ... Tôi nghĩ câu trả lời ngắn gọn là "không, nó không bị rò rỉ".(Có một cảm giác đặc biệt trong đó tất cả IEnumerables 'vô hạn' (với một cửa hàng ủng hộ cache) 'rò rỉ' (tùy thuộc vào cách bạn định nghĩa 'rò rỉ'), xem

Avoiding stack overflow (with F# infinite sequences of sequences)

cho một cuộc thảo luận thú vị của IEnumerable (aka 'seq') so với LazyList và cách người tiêu dùng có thể háo hức tiêu dùng LazyLists để 'quên' kết quả cũ để ngăn chặn một loại 'rò rỉ' nhất định.)

+1

Cảm ơn bạn, một lần nữa, Brian :) Và kudo cho phần còn lại của đội F # :) – Juliet

-1

Ứng dụng .NET không "rò rỉ" bộ nhớ theo cách này. Ngay cả khi bạn đang tạo ra nhiều đối tượng, một bộ sưu tập rác sẽ giải phóng bất kỳ đối tượng nào không có gốc rễ cho chính ứng dụng đó.

Rò rỉ bộ nhớ trong .NET thường có dạng tài nguyên không được quản lý mà bạn đang sử dụng trong ứng dụng của mình (kết nối cơ sở dữ liệu, luồng bộ nhớ, v.v.). Các trường hợp như thế này trong đó bạn tạo nhiều đối tượng và sau đó từ bỏ chúng không được coi là rò rỉ bộ nhớ vì bộ thu gom rác có thể giải phóng bộ nhớ.

+1

Việc thu gom rác thải không đảm bảo khi nào nó sẽ thu thập các vật thể này. Vì vậy, không, đây không phải là một thực hành rò rỉ bộ nhớ nhưng nó không phải là một thực hành kinh tế bộ nhớ tốt hoặc. – marr75

+0

@ marr75 - Điểm tốt và phân biệt tốt! –

+1

Cũng có khả năng IEnumerable được tạo ra có thể được cấu trúc sao cho các phần tử trước đó không đủ điều kiện để thu thập rác ngay cả sau khi chúng không còn cần thiết nữa, mà tôi sẽ xem xét rò rỉ các loại. – kvb

-1

Nó sẽ không rò rỉ bất kỳ bộ nhớ nào, nó sẽ tạo ra một chuỗi vô hạn, nhưng vì các chuỗi là IEnumerables bạn có thể liệt kê chúng mà không cần phải quan tâm đến bộ nhớ. Thực tế là đệ quy xảy ra bên trong chức năng tạo chuỗi không ảnh hưởng đến sự an toàn của đệ quy. Chỉ cần nhớ rằng trong chế độ gỡ lỗi tối ưu hóa cuộc gọi đuôi có thể bị vô hiệu hóa để cho phép gỡ lỗi đầy đủ, nhưng trong bản phát hành sẽ không có vấn đề gì.

+2

Nó phụ thuộc vào việc hàm sử dụng đệ quy đuôi hay không. Để so sánh, mã C# tương đương sẽ ném một StackOverflowException sau khoảng 15000 số nguyên: tĩnh IEnumerable startFrom (int x) {yield return x; foreach (int y trong startFrom (x + 1)) trả về y; } – Juliet

+1

Phụ lục: sau khi kiểm tra nhanh, có vẻ như mã F # * là * đuôi đệ quy. Đó là tin tốt :) – Juliet