2013-07-05 43 views
6

Trong cuốn sách C# 5.0 trong một nutshell bởi Joseph Albahari Tôi thấy điều nàyKhi nào một đối tượng đủ điều kiện thu gom rác thải?

enter image description here nơi nó nói ngay sau khi bạn vượt qua các dòng mã mà một biến là cuối cùng đã qua sử dụng, đối tượng được tham chiếu bởi nó hội đủ điều kiện cho thùng rác collection (Đó là nếu không có biến nào khác chứa tham chiếu đến đối tượng đó).

Tuy nhiên theo this bài giảng từ UC Berkley, miễn là tham chiếu đến đối tượng tồn tại trên ngăn xếp, nó sẽ không bị thu gom rác. Sự hiểu biết của tôi là, cho đến khi phương thức trả về, biến nằm trên ngăn xếp. Có nghĩa là bất kỳ đối tượng nào được tham chiếu bởi nó vẫn còn sống cho đến khi phương thức trả về.

Đây có phải là lỗi trong sách hoặc bộ sưu tập rác java và .net hoạt động khác nhau không?

+0

đọc này [MSDN bài viết] (http://msdn.microsoft.com/en-us/magazine/bb985010.aspx), bạn' sẽ tìm hiểu cách hoạt động chi tiết. –

+0

Tôi nghĩ câu trả lời được chấp nhận đã bỏ lỡ điểm. "Ngăn xếp" bạn thấy trong IL là ** không ** giống như "ngăn xếp" mà CPU thấy. Nó chỉ là một điều logic được sử dụng để mô tả tính toán; nó không thực sự tồn tại. Nó chỉ có như là một sự đơn giản hóa; mã được tối ưu hóa trước khi được JIT'ed để lắp ráp. – Mehrdad

+0

Chương 3 "Khái niệm cơ bản", phần 3.9 "Quản lý bộ nhớ tự động" trong đặc tả ngôn ngữ C# chính thức trả lời câu hỏi của bạn. Bạn có thể tải xuống từ Microsoft. http://www.microsoft.com/en-us/download/confirmation.aspx?id=7029. Tôi mất vài phút để kiểm tra. Bạn đã soạn câu hỏi của mình được bao lâu? –

Trả lời

6

Tuy nhiên theo bài giảng này từ UC Berkley, miễn là tham chiếu đến đối tượng tồn tại trên ngăn xếp, nó sẽ không bị thu gom rác.

Bạn nói đúng. Những gì bạn đang thiếu là một tham chiếu không còn tồn tại trên ngăn xếp.

Đối với mã mà xây dựng một đối tượng trên stack:

StringBuilder ref1 = new StringBuilder("object1"); 

biến ref1 được lưu trữ trên stack, trong một số vị trí bộ nhớ:

    0x403730: 
Stack Pointer -> 0x40372C: pointer to ref1 
       0x403728: saved value of EBP 
       0x403724: saved value of return address 
       0x403720 

Bây giờ đến dòng tiếp theo :

StringBuilder ref2 = new StringBuilder("object2"); 

Con trỏ đến ref2 sẽ được lưu trữ ở đâu? Trên ngăn xếp: có. Nhưng trong đó trên ngăn xếp? Trong cùng vùng bộ nhớ đã được sử dụng cho ref1 tất nhiên !:

    0x403730: 
Stack Pointer -> 0x40372C: pointer to ref2 
       0x403728: saved value of EBP 
       0x403724: saved value of return address 
       0x403720 

Sẽ ngớ ngẩn chỉ đơn giản là đẩy giá trị khác vào stack:

Stack Pointer -> 0x403730: pointer to ref2 
       0x40372C: pointer to ref1 
       0x403728: saved value of EBP 
       0x403724: saved value of return address 
       0x403720 

Nó sẽ là ngớ ngẩn bởi vì ref1 không còn cần thiết nữa.

Đó là lý do tại sao ref1 đủ điều kiện để thu thập rác: không còn bất kỳ tham chiếu nào đến nó.

+0

Tôi không chắc tại sao điều này lại bị bỏ qua, tôi đã không đọc mã cẩn thận để xem có lỗi gì không. Nhưng tôi cảm thấy như thế này là một cách quá phức tạp để giải thích một khái niệm tương đối đơn giản (ít nhất, về lý thuyết, có lẽ không phải trong thực hiện, nhưng chúng ta không * viết * một GC ở đây). –

+0

EBP là gì? Bạn đề cập đến "giá trị đã lưu của EBP" trong lời giải thích của bạn – developer747

+0

Các giá trị khác thực sự không quan trọng; tôi đã ném vào các giá trị * "có thể" * mà người ta có thể thấy trên một chồng. Trong trường hợp này tôi đã ném ra biệt ngữ * con trỏ cơ sở * (viết tắt là EBP). Theo quy ước nó trỏ đến nơi ngăn xếp là khi hàm được gọi (để con trỏ ngăn xếp (ESP) có thể được cuộn lại khi hàm trả về). tôi có thể dễ dàng nói 'up', 'down',' strange', hay 'charmed' làm nhãn cho những thứ khác; tôi chỉ muốn một cái gì đó * sorta * thực sự cho các chất liệu trang điểm của tôi. –

5

Sách chính xác.

Trong .NET, trình thu thập rác có thông tin về vị trí trong mã mà biến được sử dụng và ngay sau khi biến không được sử dụng, đối tượng đủ điều kiện để thu thập rác.

(Tuy nhiên, nếu bạn chạy mã có trình gỡ lỗi được đính kèm, trình thu thập rác sẽ thay đổi hành vi. Nó giữ đối tượng cho toàn bộ phạm vi của biến, không chỉ nơi biến được sử dụng để đối tượng đó có thể được điều tra trong trình gỡ lỗi.)

4

Hiểu biết của tôi là, cho đến khi phương thức trả về, biến nằm trên ngăn xếp. Có nghĩa là bất kỳ đối tượng nào được tham chiếu bởi nó vẫn còn sống cho đến khi phương thức trả về.

JIT được tự do loại bỏ tham chiếu đối tượng ("biến") bất kỳ lúc nào sau lần sử dụng cuối cùng, vì vậy điều này không nhất thiết phải đúng.

miễn là một tham chiếu đến đối tượng tồn tại trên stack, nó sẽ không được thu gom rác thải

Đây là sự thật - nhưng JIT có thể thay đổi khi biến này không còn "tồn tại trên ngăn xếp "theo cách không nhất thiết phải khớp với mã của bạn.

Trong C# 5, điều này cũng có thể gây nhầm lẫn, vì các phương thức async có thể được viết lại theo cách biến dài hơn bạn mong đợi trong một số trường hợp.

Điều đó đang được nói, nếu bạn cần đảm bảo rằng một đối tượng đủ điều kiện tại một số thời điểm, hãy đặt (các) biến tham chiếu đối tượng thành null cho phép bạn kiểm soát rõ ràng khi nào đủ điều kiện.

2

này là mã CIL tạo ra từ phương pháp đó:

.method private hidebysig static void Main(string[] args) cil managed 
    { 
     .entrypoint 
     // Code size  46 (0x2e) 
     .maxstack 2 
     .locals init ([0] class [mscorlib]System.Text.StringBuilder ref1, 
       [1] class [mscorlib]System.Text.StringBuilder ref2, 
       [2] class [mscorlib]System.Text.StringBuilder ref3) 
     IL_0000: nop 
     IL_0001: ldstr  "object1" 
     IL_0006: newobj  instance void [mscorlib]System.Text.StringBuilder::.ctor(string) 
     IL_000b: stloc.0  //here ref1 is stored on stack. 
     IL_000c: ldloc.0  //here it is loaded to be displayed by the console.writeline 
     IL_000d: call  void [mscorlib]System.Console::WriteLine(object) 
     IL_0012: nop  //from here on no more reference to ref1. 
     IL_0013: ldstr  "object2" 
     IL_0018: newobj  instance void [mscorlib]System.Text.StringBuilder::.ctor(string) 
     IL_001d: stloc.1 
     IL_001e: ldloc.1 
     IL_001f: stloc.2 
     IL_0020: ldloc.2 
     IL_0021: call  void [mscorlib]System.Console::WriteLine(object) 
     IL_0026: nop  //and from here on ref2 and ref3 also. 
     IL_0027: call  valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey() 
     IL_002c: pop 
     IL_002d: ret 
    } // end of method Program::Main 
Các vấn đề liên quan