Làm thế nào để một người thu gom rác tránh được một vòng lặp vô hạn ở đây?


101

Hãy xem xét chương trình C # sau, tôi đã gửi nó trên codegolf như một câu trả lời để tạo một vòng lặp mà không lặp lại:

class P{
    static int x=0;
    ~P(){
        System.Console.WriteLine(++x);
        new P();
    }
    static void Main(){
        new P();
    }
}

Chương trình này trông giống như một vòng lặp vô hạn trong quá trình kiểm tra của tôi, nhưng nó dường như chạy trong vài nghìn lần lặp, và sau đó chương trình kết thúc thành công mà không có lỗi (Không có lỗi nào được đưa ra). Nó có phải là một vi phạm đặc điểm kỹ thuật mà người hoàn thiện Pcuối cùng không được gọi?

Rõ ràng đây là đoạn mã ngu ngốc, sẽ không bao giờ xuất hiện, nhưng tôi tò mò không biết làm thế nào mà chương trình có thể hoàn thành.

Bài đăng về gôn mã gốc :: /codegolf/33196/loop-without-looping/33218#33218


49
Tôi sợ phải chạy cái này.
Eric Scherrer vào

6
Việc hoàn thiện không được gọi chắc chắn nằm trong phạm vi hành vi hợp lệ . Tuy nhiên, tôi không biết tại sao nó lại khó chịu khi chạy vài nghìn lần lặp lại, tôi hy vọng sẽ không có lệnh gọi nào.

27
CLR có khả năng bảo vệ khỏi luồng hoàn thiện không bao giờ có thể hoàn thành công việc của nó. Nó mạnh mẽ kết thúc nó sau 2 giây.
Hans Passant

2
Vì vậy, câu trả lời thực sự cho câu hỏi của bạn trong tiêu đề là nó tránh nó bằng cách chỉ để vòng lặp vô hạn chạy trong 40 giây và sau đó nó được kết thúc.
Lasse V. Karlsen

4
Từ việc thử nó, có vẻ như chương trình chỉ giết mọi thứ sau 2 giây. Trên thực tế, nếu bạn tiếp tục sinh ra các chủ đề, nó sẽ kéo dài hơn một chút :)
Michael B

Câu trả lời:


110

Theo độ Richter trong phiên bản thứ hai của CLR qua C # (vâng, tôi cần cập nhật):

Trang 478

Đối với (CLR đang ngừng hoạt động) mỗi Finalize phương thức được cung cấp khoảng hai giây để trả về. Nếu một phương thức Finalize không trả về trong vòng hai giây, CLR sẽ giết quá trình - không còn phương thức Finalize nào được gọi nữa. Ngoài ra, nếu mất hơn 40 giây để gọi các phương thức Finalize của tất cả các đối tượng , một lần nữa, CLR sẽ giết quá trình.

Ngoài ra, như Servy đã đề cập, nó có một chuỗi riêng.


5
Mỗi phương thức finalize trong mã này mất đáng kể dưới 40 giây cho mỗi đối tượng. Việc một đối tượng mới được tạo và sau đó đủ điều kiện để hoàn thiện không liên quan đến trình hoàn thiện hiện tại.
Jacob Krall

2
Đây thực sự không phải là những gì hoàn thành công việc. Cũng có một khoảng thời gian chờ trên hàng đợi có thể tự do được làm trống khi tắt máy. Đó là những gì mã này không thành công, nó tiếp tục thêm các đối tượng mới vào hàng đợi đó.
Hans Passant

Chỉ cần suy nghĩ về điều này, không phải làm trống hàng đợi có thể tự do giống như "Ngoài ra, nếu mất hơn 40 giây để gọi các phương thức Finalize của tất cả các đối tượng, một lần nữa, CLR sẽ giết quá trình."
Eric Scherrer

23

Trình hoàn thiện không chạy trong luồng chính. Trình hoàn thiện có luồng riêng chạy mã và nó không phải là luồng nền trước sẽ giữ cho ứng dụng chạy. Luồng chính hoàn thành một cách hiệu quả ngay lập tức, tại thời điểm đó, luồng hoàn thiện chỉ đơn giản chạy nhiều lần nếu có cơ hội trước khi quy trình bị phá bỏ. Không có gì là giữ cho chương trình tồn tại.


Nếu trình hoàn thiện chưa hoàn thành 40 giây sau khi chương trình đáng lẽ đã thoát do không có luồng chính nào còn sống, thì nó sẽ bị kết thúc và quá trình sẽ kết thúc. Mặc dù vậy, đây là những giá trị cũ nên Microsoft có thể đã điều chỉnh các con số thực tế hoặc thậm chí toàn bộ thuật toán. Se blog.stephencleary.com/2009/08/finalizers-at-process-exit.html
Lasse V. Karlsen

@ LasseV.Karlsen Đó có phải là hành vi được lập thành văn bản của ngôn ngữ hay đơn giản là cách MS chọn triển khai các trình hoàn thiện của họ làm chi tiết triển khai? Tôi mong đợi cái sau.
Servy

Tôi cũng mong đợi điều sau. Tài liệu tham khảo chính thức nhất về hành vi này mà tôi đã thấy là những gì Eric ở trên đã đăng trong câu trả lời của anh ấy, từ cuốn sách CLR via C # của Jeffrey Richter.
Lasse V. Karlsen,

8

Bộ thu gom rác không phải là một hệ thống hoạt động. Nó chạy "đôi khi" và chủ yếu là theo yêu cầu (ví dụ: khi tất cả các trang do HĐH cung cấp đều đã đầy).

Hầu hết các trình thu gom rác chạy theo cách giống như thế hệ thứ nhất theo chiều rộng trong một luồng con. Trong hầu hết các trường hợp, có thể mất hàng giờ trước khi đồ vật được tái chế.

Sự cố duy nhất xảy ra khi bạn muốn chấm dứt chương trình. Tuy nhiên đó không thực sự là một vấn đề. Khi bạn sử dụng killmột hệ điều hành sẽ yêu cầu một cách lịch sự để chấm dứt các quy trình. Tuy nhiên, khi quy trình vẫn hoạt động, người ta có thể sử dụngkill -9 nơi Hệ điều hành loại bỏ tất cả quyền kiểm soát.

Khi tôi chạy mã của bạn trong csharpmôi trường tương tác , tôi có:

csharp>  

1
2

Unhandled Exception:
System.NotSupportedException: Stream does not support writing
  at System.IO.FileStream.Write (System.Byte[] array, Int32 offset, Int32 count) [0x00000] in <filename unknown>:0 
  at System.IO.StreamWriter.FlushBytes () [0x00000] in <filename unknown>:0 
  at System.IO.StreamWriter.FlushCore () [0x00000] in <filename unknown>:0 
  at System.IO.StreamWriter.Write (System.Char[] buffer, Int32 index, Int32 count) [0x00000] in <filename unknown>:0 
  at System.IO.CStreamWriter.Write (System.Char[] buffer, Int32 index, Int32 count) [0x00000] in <filename unknown>:0 
  at System.IO.CStreamWriter.Write (System.Char[] val) [0x00000] in <filename unknown>:0 
  at System.IO.CStreamWriter.Write (System.String val) [0x00000] in <filename unknown>:0 
  at System.IO.TextWriter.Write (Int32 value) [0x00000] in <filename unknown>:0 
  at System.IO.TextWriter.WriteLine (Int32 value) [0x00000] in <filename unknown>:0 
  at System.IO.SynchronizedWriter.WriteLine (Int32 value) [0x00000] in <filename unknown>:0 
  at System.Console.WriteLine (Int32 value) [0x00000] in <filename unknown>:0 
  at P.Finalize () [0x00000] in <filename unknown>:0

Do đó, chương trình của bạn bị treo do stdoutbị chặn bởi sự khắc nghiệt của môi trường.

Khi gỡ bỏ Console.WriteLinevà giết chương trình. Sau năm giây, chương trình sẽ kết thúc (nói cách khác, trình thu gom rác từ bỏ và đơn giản là sẽ giải phóng tất cả bộ nhớ mà không cần tính đến các trình hoàn thiện).


Điều này thật thú vị khi csharp tương tác bùng nổ vì những lý do hoàn toàn khác nhau. Đoạn mã chương trình ban đầu không có bảng điều khiển writeline, tôi tò mò không biết liệu nó có kết thúc hay không.
Michael B

@MichaelB: Tôi cũng đã thử nghiệm điều này (xem bình luận bên dưới). Nó đợi trong năm giây và sau đó kết thúc. Tôi đoán rằng phần cuối cùng của Ptrường hợp đầu tiên chỉ đơn giản là hết thời gian.
Willem Van Onsem
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.