Bagaimana pengumpul sampah menghindari pengulangan tak terbatas di sini?

101

Pertimbangkan program C # berikut, saya mengirimkannya ke codegolf sebagai jawaban untuk membuat loop tanpa perulangan:

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

Program ini terlihat seperti loop tak terbatas dalam pemeriksaan saya, tetapi tampaknya berjalan untuk beberapa ribu iterasi, dan kemudian program berakhir dengan sukses tanpa kesalahan (Tidak ada kesalahan yang terjadi). Apakah pelanggaran spesifikasi yang Pakhirnya tidak dipanggil oleh finalizer ?

Jelas ini adalah kode bodoh, yang seharusnya tidak pernah muncul, tapi saya penasaran bagaimana program itu bisa selesai.

Pos kode golf asli :: /codegolf/33196/loop-without-looping/33218#33218

Michael B
sumber
49
Saya takut menjalankan ini.
Eric Scherrer
6
Bahwa seorang finalisator tidak dipanggil pasti dalam ranah perilaku yang valid . Saya tidak tahu mengapa harus menjalankan beberapa ribu iterasi, saya mengharapkan nol pemanggilan.
27
CLR memiliki perlindungan terhadap thread finalizer yang tidak pernah dapat menyelesaikan tugasnya. Ini secara paksa menghentikannya setelah 2 detik.
Hans Passant
2
Jadi jawaban sebenarnya atas pertanyaan Anda dalam judul adalah ia menghindarinya dengan membiarkan loop tak terbatas berjalan selama 40 detik dan kemudian diakhiri.
Lasse V. Karlsen
4
Dari mencobanya, tampaknya program tersebut hanya mematikan semuanya setelah 2 detik apa pun yang terjadi. Sebenarnya jika Anda terus memijah utas, itu akan bertahan lebih lama :)
Michael B

Jawaban:

110

Sesuai Richter di CLR edisi kedua via C # (ya saya perlu memperbarui):

Halaman 478

Untuk (CLR dimatikan) setiap metode Finalisasi diberikan waktu sekitar dua detik untuk kembali. Jika metode Finalize tidak kembali dalam dua detik, CLR hanya menghentikan proses - tidak ada lagi metode Finalize yang dipanggil. Juga, jika dibutuhkan lebih dari 40 detik untuk memanggil metode Finalisasi semua objek , sekali lagi, CLR hanya menghentikan proses.

Juga, seperti yang disebutkan Servy, ia memiliki utasnya sendiri.

Eric Scherrer
sumber
5
Setiap metode penyelesaian dalam kode ini membutuhkan waktu kurang dari 40 detik per objek. Bahwa objek baru dibuat dan kemudian memenuhi syarat untuk finalisasi tidak relevan dengan finalizer saat ini.
Jacob Krall
2
Sebenarnya bukan ini yang menyelesaikan pekerjaan. Ada juga batas waktu pada antrian freachable yang dikosongkan saat dimatikan. Di mana kode ini gagal, ia terus menambahkan objek baru ke antrian itu.
Hans Passant
Hanya memikirkan hal ini, tidak mengosongkan antrean freachable sama dengan "Selain itu, jika diperlukan lebih dari 40 detik untuk memanggil metode Finalisasi semua objek, sekali lagi, CLR akan menghentikan prosesnya."
Eric Scherrer
23

Finalizer tidak berjalan di utas utama. Finalizer memiliki utasnya sendiri yang menjalankan kode, dan ini bukan utas latar depan yang akan membuat aplikasi tetap berjalan. Utas utama selesai secara efektif segera, di mana utas finalizer hanya berjalan sebanyak yang mendapat kesempatan sebelum proses dirobohkan. Tidak ada yang membuat program tetap hidup.

Pelayanan
sumber
Jika finalizer belum menyelesaikan 40 detik setelah program seharusnya keluar karena tidak ada utas utama yang hidup, itu akan dihentikan dan proses akan dihentikan. Ini adalah nilai-nilai lama jadi Microsoft mungkin telah mengubah angka sebenarnya atau bahkan keseluruhan algoritmanya sekarang. Lihat blog.stephencleary.com/2009/08/finalizers-at-process-exit.html
Lasse V. Karlsen
@ LasseV.Karlsen Apakah itu perilaku bahasa yang terdokumentasi, atau hanya bagaimana MS memilih untuk mengimplementasikan finalizer mereka sebagai detail implementasi? Saya mengharapkan yang terakhir.
Pelayanan
Saya mengharapkan yang terakhir juga. Referensi paling resmi untuk perilaku ini yang pernah saya lihat adalah apa yang Eric posting di atas dalam jawabannya, dari buku CLR via C # oleh Jeffrey Richter.
Lasse V. Karlsen
8

Pengumpul sampah bukanlah sistem yang aktif. Ini berjalan "kadang-kadang" dan sebagian besar sesuai permintaan (misalnya ketika semua halaman yang ditawarkan oleh OS penuh).

Sebagian besar pengumpul sampah menjalankan dengan cara seperti generasi pertama di subjudul. Biasanya dibutuhkan waktu berjam-jam sebelum objek tersebut didaur ulang.

Satu-satunya masalah terjadi ketika Anda ingin menghentikan program. Namun itu sebenarnya bukan masalah. Ketika Anda menggunakan killOS akan meminta dengan sopan untuk menghentikan proses. Namun ketika proses tetap aktif, seseorang dapat menggunakan di kill -9mana Sistem Operasi menghapus semua kontrol.

Ketika saya menjalankan kode Anda di csharplingkungan interaktif , saya mendapatkan:

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

Dengan demikian program Anda macet karena stdoutdiblokir oleh penghentian lingkungan.

Saat menghapus Console.WriteLinedan mematikan program. Ini setelah lima detik program berakhir (dengan kata lain, pengumpul sampah menyerah dan hanya akan membebaskan semua memori tanpa memperhitungkan finalizer).

Willem Van Onsem
sumber
Ini menarik bahwa csharp interaktif meledak karena alasan yang sama sekali berbeda. Potongan program asli tidak memiliki garis tulis konsol, saya ingin tahu apakah itu akan dihentikan juga.
Michael B
@MichaelB: Saya telah menguji ini juga (lihat komentar di bawah). Itu menunggu selama lima detik dan kemudian berhenti. Saya kira finalizer dari Pinstance pertama hanya kehabisan waktu.
Willem Van Onsem