Сборка мусора. Странное поведение. Утечка.
От: igor-booch Россия  
Дата: 13.06.20 14:35
Оценка:
В приложении есть класс с финилайзером (КФ).
КФ ссылается на экземпляры класса К1.
Экземпляр класса К2 ссылается на экземпляр класса К1.
Финалайзер КФ убирает ссылку К2 на К1.

При старте приложении создаётся большое количество объетов К2. Они нужны на всём протяжении работы приложения.
Далее происходят циклы:
1) Создаётся большое количество объектов КФ и К1.
2) Ссылки на КФ убираются

После каждого цикла объекты КФ из памяти не выгружаются, их финалайзеры не вызываются.
НО! Если перед началом циклов вызвать

GC.Collect();


то УТЕЧКИ НЕ ПРОИСХОДИТ.

Если не вызвать этот код, то JetBrains dotMemory Profiler показывает, что все объекты КФ доступны только из f-reacheble queue,
то есть ссылок на них нет, остальсь только вызвать у них финалайзер,
но GC почему-то этого делать не хочет.
Скриншоты с сессиями профилирования здесь.
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Отредактировано 13.06.2020 16:39 igor-booch . Предыдущая версия . Еще …
Отредактировано 13.06.2020 16:16 igor-booch . Предыдущая версия .
Отредактировано 13.06.2020 14:53 igor-booch . Предыдущая версия .
Re: Сборка мусора. Странное поведение. Утечка.
От: Sharowarsheg  
Дата: 13.06.20 14:56
Оценка:
Здравствуйте, igor-booch, Вы писали:


IB>но GC почему-то этого делать не хочет.


А сколько памяти свободно? Может, и не нужно убирать?
Re[2]: Сборка мусора. Странное поведение. Утечка.
От: igor-booch Россия  
Дата: 13.06.20 15:19
Оценка:
Здравствуйте, Sharowarsheg, Вы писали:

S>Здравствуйте, igor-booch, Вы писали:



IB>>но GC почему-то этого делать не хочет.


S>А сколько памяти свободно? Может, и не нужно убирать?


Нет, приложение в конечном итоге падает при достижении около 3.37 Gb
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re[3]: Сборка мусора. Странное поведение. Утечка.
От: night beast СССР  
Дата: 13.06.20 15:29
Оценка:
Здравствуйте, igor-booch, Вы писали:

IB>>>но GC почему-то этого делать не хочет.


S>>А сколько памяти свободно? Может, и не нужно убирать?


IB>Нет, приложение в конечном итоге падает при достижении около 3.37 Gb


у нета какие-то странные алгоритмы сборки мусора.
возможно, твои объекты оказались в третьем поколении (dotMemory Profiler вроде умеет поколения по разному подкрашивать) и сборщик, на основании сборки предыдущих, решает их не собирать?

есть возможность вручную запустить сборку, но это наверно не правильно.
Re[4]: Сборка мусора. Странное поведение. Утечка.
От: igor-booch Россия  
Дата: 13.06.20 15:46
Оценка:
Здравствуйте, night beast, Вы писали:

NB>Здравствуйте, igor-booch, Вы писали:


IB>>>>но GC почему-то этого делать не хочет.


S>>>А сколько памяти свободно? Может, и не нужно убирать?


IB>>Нет, приложение в конечном итоге падает при достижении около 3.37 Gb


NB>у нета какие-то странные алгоритмы сборки мусора.

NB>возможно, твои объекты оказались в третьем поколении (dotMemory Profiler вроде умеет поколения по разному подкрашивать) и сборщик, на основании сборки предыдущих, решает их не собирать?

NB>есть возможность вручную запустить сборку, но это наверно не правильно.


Ручная сборка после запусков циклов ничего не даёт. См. скриншоты.
До запусков циклов ручная сборка с помощью JetBrains dotMemory Profiler даёт всё (точнее после неё код ведёт себя ожидаемым образом).
Только я не знаю как JetBrains dotMemory Profiler делаёт ручную сборку, иначе бы са в коде её вызывал.
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re[4]: Сборка мусора. Странное поведение. Утечка.
От: igor-booch Россия  
Дата: 13.06.20 16:17
Оценка:
обновил вопрос
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re: Сборка мусора. Странное поведение. Утечка.
От: Слава  
Дата: 13.06.20 16:55
Оценка:
Здравствуйте, igor-booch, Вы писали:

IB>В приложении есть класс с финилайзером (КФ).

IB>но GC почему-то этого делать не хочет.

Постарайтесь сделать минимально возможный пример для воспроизведения бага. И немного непонятно, для чего финалайзер, что у вас там такое.
Re[5]: Сборка мусора. Странное поведение. Утечка.
От: night beast СССР  
Дата: 13.06.20 18:05
Оценка:
Здравствуйте, igor-booch, Вы писали:

NB>>есть возможность вручную запустить сборку, но это наверно не правильно.


IB>Ручная сборка после запусков циклов ничего не даёт. См. скриншоты.


попробуй несколько раз подряд запустить
Re[6]: Сборка мусора. Странное поведение. Утечка.
От: igor-booch Россия  
Дата: 13.06.20 20:09
Оценка:
Здравствуйте, night beast, Вы писали:

NB>Здравствуйте, igor-booch, Вы писали:


NB>>>есть возможность вручную запустить сборку, но это наверно не правильно.


IB>>Ручная сборка после запусков циклов ничего не даёт. См. скриншоты.


NB>попробуй несколько раз подряд запустить


Пробовал
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re: Сборка мусора. Странное поведение. Утечка.
От: takTak  
Дата: 13.06.20 20:47
Оценка:
если ты ещё не попробовал,
WeakReference<T>
то я бы на твоём месте с этим поигрался бы

вот тут примеры для "подумать" :

http://tooslowexception.com/do-we-need-jvms-phantomreference-in-net/
https://ayende.com/blog/185889-A/implementing-phantom-reference-in-c


https://medium.com/criteo-labs/implementing-java-referencequeue-and-phantomreference-in-c-827d7141b6e4

Отредактировано 13.06.2020 21:15 takTak . Предыдущая версия . Еще …
Отредактировано 13.06.2020 21:08 takTak . Предыдущая версия .
Re: Сборка мусора. Странное поведение. Утечка.
От: takTak  
Дата: 13.06.20 21:49
Оценка: 13 (2)
IB>В приложении есть класс с финилайзером (КФ).
IB>Финалайзер КФ убирает ссылку К2 на К1.


Writing High Performance .NET Code:
Avoid Finalizers
Never implement a finalizer unless it is required. Finalizers are code, triggered by the garbage collector to cleanup unmanaged resources. They are called from a single thread, one after the other, and only after the garbage collector declares the object dead after a collection. This means that if your class implements a finalizer, you are guaranteeing that it will stay in memory even after the collection that should have killed it. This decreases overall GC efficiency and ensures that your program will dedicate CPU resources to cleaning up your object.
If you do implement a finalizer, you must also implement the IDisposable interface to enable explicit cleanup, and call GC.SuppressFinalize(this) in the Dispose method to remove the object from the finalization queue. As long as you call Dispose before the next collection, then it will clean up the object properly without the need for the finalizer to run. The following example correctly demonstrates this pattern.




class Foo : IDisposable 
{   
    ~Foo()   
    {     
        Dispose(false);   
    }      
    
    public void Dispose()   
    {     
        Dispose(true);     
        GC.SuppressFinalize(this);   
    }      
    
    protected virtual void Dispose(bool disposing)   
    {     
        if (disposing)    
        {       
            this.managedResource.Dispose();     
        } 

        // Cleanup unmanaged resourced     
        UnsafeClose(this.handle);     
        
        // If the base class is IDisposable object      
        // make sure you call:     
        //base.Dispose(disposing);   
    }   
        
}


Note Some people think that finalizers are guaranteed to run. This is generally true, but not absolutely so.
If a program is force-terminated then no more code runs and the process dies immediately.
There is also a time limit to how long all of the finalizers are given on process shutdown.
If your finalizer is at the end of the list, it may be skipped. Moreover, because finalizers execute sequentially,
if another finalizer has an infinite loop bug in it, then no finalizers after it will ever run.
While finalizers are not run on a GC thread, they are triggered by a GC so if you have no collections,
the finalizers will not run. Therefore, you should not rely on finalizers to clean up state external to your process.
Re: Сборка мусора. Странное поведение. Утечка.
От: GlebZ Россия  
Дата: 13.06.20 23:53
Оценка:
Здравствуйте, igor-booch, Вы писали:

Когда-то в стародавние времена была такая ошибка. Если в финалайзере возникал exception то дальнейшая финализация не производилась. Сейчас не знаю, поправили или нет. Но очень похожее поведение.
Re[2]: Сборка мусора. Странное поведение. Утечка.
От: igor-booch Россия  
Дата: 14.06.20 07:11
Оценка:
T>While finalizers are not run on a GC thread, they are triggered by a GC so if you have no collections, the finalizers will not run.

Это не понятно. Какие коллекции?
В каком потоке запускаются финализаторы?
И как это связано с тем, что не запускаются финализаторы?
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re[2]: Сборка мусора. Странное поведение. Утечка.
От: igor-booch Россия  
Дата: 14.06.20 07:12
Оценка:
Здравствуйте, GlebZ, Вы писали:

GZ>Здравствуйте, igor-booch, Вы писали:


GZ>Когда-то в стародавние времена была такая ошибка. Если в финалайзере возникал exception то дальнейшая финализация не производилась. Сейчас не знаю, поправили или нет. Но очень похожее поведение.


exception в финализаторе нет
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re[3]: Сборка мусора. Странное поведение. Утечка.
От: takTak  
Дата: 14.06.20 07:39
Оценка:
T>>While finalizers are not run on a GC thread, they are triggered by a GC so if you have no collections, the finalizers will not run.

IB>Это не понятно. Какие коллекции?

имеются в в виду не коллекции, а сборка мусора : GC.Collect
IB>В каком потоке запускаются финализаторы?
это не важно
IB>И как это связано с тем, что не запускаются финализаторы?
важно то, что конструкция ненадёжна: все finilizers выполняются последовательно и если хотя бы один подвиснет, то весь процесс освобождения памяти прекратится

у тебя очень замысловатая структура нахождения в памяти объектов, я поэтому и указывал на weak reference как более подходящий по дизайну подход, finalize вообще-то должны использоваться для освобождения unmanaged resources, а ты используешь их для совсем другого
Re: Сборка мусора. Странное поведение. Утечка.
От: takTak  
Дата: 14.06.20 07:52
Оценка:
IB>После каждого цикла объекты КФ из памяти не выгружаются, их финалайзеры не вызываются.
IB>НО! Если перед началом циклов вызвать

IB>
IB>GC.Collect();
IB>


IB>то УТЕЧКИ НЕ ПРОИСХОДИТ.


IB>Если не вызвать этот код, то JetBrains dotMemory Profiler показывает, что все объекты КФ доступны только из f-reacheble queue,

IB>то есть ссылок на них нет, остальсь только вызвать у них финалайзер,
IB>но GC почему-то этого делать не хочет.


When you have a finalizer in your class, objects of those classes are moved to the finalization queue. If the objects are reachable, they are moved to the "Freachable" queue. The GC reclaims the memory occupied by objects that are not reachable. Periodically, the GC checks if the objects that reside in the "Freachable" queue are reachable. If they are not reachable, memory occupied by those objects are reclaimed.


т.е. если объекты, находящиеся в "Freachable" queue, могут быть локализированы, никакого освобождения памяти и не должно происходить

A finalizer is implicitly called when the memory occupied by the object is reclaimed. However, a finalizer is not guaranteed to be called by the GC — it may or may not be called at all. In essence, a finalizer works on a non-deterministic mode – the runtime doesn’t guarantee that a finalizer would be called at all.
Re[2]: Сборка мусора. Странное поведение. Утечка.
От: alexzzzz  
Дата: 14.06.20 09:39
Оценка: +1
Здравствуйте, takTak, Вы писали:

T>A finalizer is implicitly called when the memory occupied by the object is reclaimed. However, a finalizer is not guaranteed to be called by the GC — it may or may not be called at all. In essence, a finalizer works on a non-deterministic mode – the runtime doesn’t guarantee that a finalizer would be called at all.


Но все эти предупреждения про то, что исполнение финализаторов не гарантируется, не означают, что рантайм не будет честно пытаться их выполнить. Все описываемые случаи, когда выполнение финализаторов не происходит, связаны с какими-то нештатными ситуациями. Точно так же можно утверждать, что рантайм не гарантирует выполнение операции умножения ― ведь где-то раньше в том же потоке может возникнуть исключение, поток грохнется и все последующие операции умножения в нём не выполнятся. Или память кончится, или стек потока переполнится, или электричество пропадёт...

Если ничего нештатного не происходит, то выполнение
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

должно вполне детерминированно
1) запустить сборку мусора;
2) найти весь мусор с финализаторами и отправить эти финализаторы в очередь на выполнение в отдельном потоке финализации;
3) дождаться их завершения;
4) ещё раз проверить мусор, и если объекты с финализаторами не воскресли, а всё ещё остаются мусором, удалить их тоже.
Отредактировано 14.06.2020 9:48 alexzzzz . Предыдущая версия . Еще …
Отредактировано 14.06.2020 9:47 alexzzzz . Предыдущая версия .
Отредактировано 14.06.2020 9:46 alexzzzz . Предыдущая версия .
Отредактировано 14.06.2020 9:44 alexzzzz . Предыдущая версия .
Re[3]: Сборка мусора. Странное поведение. Утечка.
От: takTak  
Дата: 14.06.20 10:27
Оценка:
T>>A finalizer is implicitly called when the memory occupied by the object is reclaimed. However, a finalizer is not guaranteed to be called by the GC — it may or may not be called at all. In essence, a finalizer works on a non-deterministic mode – the runtime doesn’t guarantee that a finalizer would be called at all.

A>Но все эти предупреждения про то, что исполнение финализаторов не гарантируется, не означают, что рантайм не будет честно пытаться их выполнить. Все описываемые случаи, когда выполнение финализаторов не происходит, связаны с какими-то нештатными ситуациями. Точно так же можно утверждать, что рантайм не гарантирует выполнение операции умножения ― ведь где-то раньше в том же потоке может возникнуть исключение, поток грохнется и все последующие операции умножения в нём не выполнятся. Или память кончится, или стек потока переполнится, или электричество пропадёт...


A>Если ничего нештатного не происходит, то выполнение

A>
GC.Collect();
A>GC.WaitForPendingFinalizers();
A>GC.Collect();

A>должно вполне детерминированно
A>1) запустить сборку мусора;
A>2) найти весь мусор с финализаторами и отправить эти финализаторы в очередь на выполнение в отдельном потоке финализации;
A>3) дождаться их завершения;
A>4) ещё раз проверить мусор, и если объекты с финализаторами не воскресли, а всё ещё остаются мусором, удалить их тоже.

так я с тобой и не спорю, но вот сам подумай: когда ты вынужден из твоего собственного кода 2 раза подряд вызывать GC.Collect() , то у тебя не остаётся после такого кода осадочка? ведь ты мог и сам детерминистически вызвать Dispose() только для тех объектов, которые и должны быть удалены, не полагаясь на какую-то мало кому понятную магию

в топике же речь не об этом, а а том, что находящиеся в finalization queue объекты для GC не являются подлежащими удалению... по какой причине это происходит!? без бутылки выложенного на гитхабе маленького примера об этом можно только гадать
Re[4]: Сборка мусора. Странное поведение. Утечка.
От: alexzzzz  
Дата: 14.06.20 11:12
Оценка: 21 (1) +1
Здравствуйте, takTak, Вы писали:

T>так я с тобой и не спорю, но вот сам подумай: когда ты вынужден из твоего собственного кода 2 раза подряд вызывать GC.Collect() , то у тебя не остаётся после такого кода осадочка? ведь ты мог и сам детерминистически вызвать Dispose() только для тех объектов, которые и должны быть удалены, не полагаясь на какую-то мало кому понятную магию


Через насильный
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

объекты с финализаторами должны удаляться (если ссылок на них действительно нет). Если они не удаляются, значит происходит что-то нештатное и с причиной стоит разобраться независимо от того, уместно в данном случае использование финализаторов или не особо.

Финализаторы ― штуки вообще прикольные. Можно сделать секретный зомби-объект, который создаётся где-нибудь в неочевидном месте (инициализатор модуля отлично подходит), на который ниоткуда ссылок нет, но сборщик мусора удалить его не может, потому что объект в своём финализаторе сам себя воскрешает, регистрируя на повторную финализацию через GC.ReRegisterForFinalize(). Вот кстати и способ создать себе непонятную утечку памяти.
Re[5]: Сборка мусора. Странное поведение. Утечка.
От: takTak  
Дата: 14.06.20 11:54
Оценка:
A>объекты с финализаторами должны удаляться (если ссылок на них действительно нет).

понятно, что там, скорее всего, какая-то проблема со ссылками, для этого и существует:

weak reference, which references an object while still allowing that object to be reclaimed by garbage collection... A weak reference allows the garbage collector to collect an object while still allowing an application to access the object. If you need the object, you can still obtain a strong reference to it and prevent it from being collected.

Отредактировано 14.06.2020 11:57 takTak . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.