Як тут збирач сміття уникає нескінченного циклу?


101

Розглянемо наступну програму C #, я подав її на codegolf як відповідь, щоб створити цикл без циклу:

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

Ця програма виглядає як нескінченний цикл в моєму огляді, але вона, здається, працює на кілька тисяч ітерацій, і тоді програма успішно припиняється без помилок (помилок не викидається). Чи є специфічним порушенням те, що остаточний фіналізатор Pне називається?

Зрозуміло, що це дурний код, який ніколи не повинен з’являтися, але мені цікаво, як програма могла коли-небудь завершитись.

Оригінальний код посту для гольфу :: /codegolf/33196/loop-without-looping/33218#33218


49
Боюся запустити це.
Ерік Шеррер

6
Те, що фіналізатор не викликається, безумовно, в межах дійсної поведінки . Я не знаю, чому це турбує виконувати кілька тисяч ітерацій, однак я очікую нульових викликів.

27
CLR має захист від нитки фіналізатора, яка ніколи не може закінчити свою роботу. Це насильно припиняє його через 2 секунди.
Ганс Пасант

2
Тож справжня відповідь на ваше запитання в заголовку полягає в тому, що його уникнути, просто давши нескінченному циклу працювати 40 секунд, а потім він припиняється.
Лассе В. Карлсен

4
З його спроби здається, що програма просто вбиває все через 2 секунди, незалежно від того. Насправді, якщо ви будете тримати нерестові нитки, це триватиме трохи довше :)
Michael B

Відповіді:


110

Відповідно до Ріхтера у другій редакції CLR через C # (так, мені потрібно оновити):

Сторінка 478

Для (CLR вимикається) кожному методу Finalize надається приблизно дві секунди, щоб повернутися. Якщо метод Finalize не повертається протягом двох секунд, CLR просто вбиває процес - більше методів Finalize не викликається. Крім того, якщо для виклику методів доопрацювання всіх об'єктів потрібно більше 40 секунд , CLR просто вбиває процес.

Також, як згадує Сервій, у нього є своя нитка.


5
Кожен метод доопрацювання у цьому коді значно за 40 секунд на об’єкт. Те, що новий об’єкт створений і потім придатний для доопрацювання, не стосується поточного фіналізатора.
Джейкоб Кралл

2
Це насправді не те, що робить роботу. Існує також тайм-аут на черговій черзі, яка споживається, і вимкнеться при відключенні. Що не дає цього коду, він постійно додає нові об’єкти до цієї черги.
Ганс Пасант

Думаючи про це, чи не випорожнюється чергова черга такою ж, як "Крім того, якщо для виклику всіх об'єктів знадобиться більше 40 секунд" Завершити методи, знову ж таки, CLR просто вбиває процес ".
Ерік Шеррер

23

Фіналізатор не працює в основному потоці. Фіналізатор має власний потік, який запускає код, і це не нитка переднього плану, яка б підтримувала роботу програми. Основна нитка завершується ефективно відразу, і тоді фінілайзер нитка просто запускається стільки разів, скільки отримує шанс, перш ніж процес буде зірваний. Ніщо не підтримує програму в живих.


Якщо фіналізатор не завершився 40 секунд після того, як програма повинна була вийти з-за відсутності основної нитки, вона буде припинена і процес закінчиться. Це старі значення, хоча Microsoft, можливо, вже змінила фактичні числа або навіть весь алгоритм. Se blog.stephencleary.com/2009/08/finalizers-at-process-exit.html
Lasse V. Karlsen

@ LasseV.Karlsen Це задокументована поведінка мови, чи просто, як MS вирішили застосувати свої фіналізатори як деталі реалізації? Я б очікував останнього.
Серві

Очікую і останнього. Офіційна згадка про цю поведінку, яку я бачив, - це те, що Ерік виклав вище у своїй відповіді, із книги CLR через C # Джеффрі Ріхтера.
Лассе В. Карлсен

8

Збирач сміття не є активною системою. Він працює "іноді" і в основному на вимогу (наприклад, коли всі сторінки, пропоновані ОС заповнені).

Більшість сміттєзбірників працює в ширині першого покоління в підточці. У більшості випадків, перш ніж об’єкт переробляти, може пройти години.

Єдина проблема виникає, коли ви хочете припинити програму. Однак це насправді не проблема. При використанні killОС буде ввічливо попросити припинити процеси. Якщо процес залишається активним, можна використовувати kill -9там, де Операційна система видаляє все управління.

Коли я запустив ваш код в інтерактивному csharpсередовищі, я отримав:

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

Таким чином, ваша програма виходить з ладу, оскільки stdoutблокується завершенням оточення.

При видаленні Console.WriteLineта вбивстві програми. Через п'ять секунд програма припиняється (іншими словами, сміттєзбірник здається і просто звільнить всю пам'ять, не враховуючи фіналізаторів).


Це захоплююче, що інтерактивний кешп вибухає з зовсім інших причин. У початковому фрагменті програми не було написано консолі, мені цікаво, чи він також закінчиться.
Майкл Б

@MichaelB: Я також тестував це (див. Коментар нижче). Він чекає п'ять секунд, а потім припиняється. Я думаю, фіналізатор першої Pінстанції просто вийшов.
Віллем Ван Онсем
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.