tgoop.com/gdb_dbg/31
Last Update:
Люблю запах финализаторов по утрам! (часть 1)
По моему опыту финализаторы - это одна из самых проблемных тем в managed языках. Из-за них рождается какое-то невозможное количество багов, неоправдавшихся надежд и разбитых мечтаний разработчиков (как разработчиков приложений, так и виртуальных машин). И у меня даже есть ответ на вопрос "почему" Но обо всем по порядку.
О чем речь: в большинстве языков программирования со сборкой мусора есть возможность зарегистрировать callback, который будет вызываться при смерти объекта, точнее - чуть раньше этой самой смерти. А что такое смерть в случае языка с GC? Правильно - это момент, когда GC решает его собрать.
Идея кажется буквально очевидной. Особенно, если вы пришли из мира C++. Вот там у классов есть деструкторы, они вызываются, когда объекты помирают (известно когда), давайте повторим этот фокус и для Java. Или C#, там даже синтаксис как у деструкторов:
class Zombie
{
~Zombie()
{
System.Diagnostics.Trace.WriteLine("dying...");
}
}
Вот что тут может пойти не так? На самом же деле фича буквально открывает врата в ад. Приведу несколько (но далеко не все!) примеров различных проблем. Начнем с не самой очевидной но очень неприятной.
Производительность и memory drag.
Когда разработчики пишут финализаторы, они обычно не задумываются, чего это будет им стоить. Что в целом то правильно, как я уже говорил раньше, языковая фича в теории не должна оглядываться на реализацию, а значит производительность - это головная боль рантайм инженеров. Но давайте подумаем: как вообще можно реализовать финализаторы?
Раз смерть объекта - это момент, когда за ним приходит GC, то мы должны вмешаться в работу GC и буквально вытащить объект с финализатором из пасти льва, т.е. GC. Вместо того, чтобы объект собирать, мы вынуждены дать ему еще пожить, пока будет выполняться этот самый callback. Это необходимо, т.к. объект может использоваться в коде финализатора, а значит он точно переживет как минимум эту сборку GC. Далее осознаем, что финализаторов ведь может быть много, и все нужно исполнить, т.е. может накопиться целая очередь объектов, которые давно бы померли, но пока ждут своей очереди в чистилище. А теперь добавьте к этому тот факт, что не только сам объект застрял в Лимбе, но и все достижимые только из него объекты. Получаем тонкое место, потенциально очень долгую очередь из умирающих объектов и memory drag (удержание памяти, которую уже могли бы освободить).
Я уж промолчу о том, что есть возможность воскрешать объекты в финализаторах, т.е. создающие новые ссылки на них. Это особая головная боль и неочевидное поведение, но бог авторам такого кода судья.
Как managed языки пытаются такую проблему исправить?
- Java: метод
finalize()
в java.lang.Object
уже давно @Deprecated
. Вместо него предлагается использовать java.lang.ref.Cleaner
, работающий на слабых ссылках с очередями. Я довольно подробно рассказывал про это вот здесь. Если коротко, то идея в том, чтобы отделить callback от объекта, не хранить в нем ссылки на сам объект и таким образом срезать memory drag;- Python: начиная с 3.4 вдобавок к старым финализаторам (
_del_
) ввели weakref.finalize(...)
, который делает тоже самое, что и Java: регистрирует именно callback связанный с объектом только слабой ссылкой. Поэтому (в случае, когда объект будет собран в рамках поиска циклического мусора, а только там это проблема) callback, будет вызван, но объект при этом задержан в памяти не будет (конечно, если не делать глупостей);- Golang: тут красиво. Во-первых, как и с
Cleaner
-ами в Java и weakref.finalize
в Python, финализатор - это не свойство класса, но свойство объекта, которое стоит указывать при его создании (ну или когда вам удобно). А во-вторых, в документации явно указывается, что исполнение финализаторов - это узкое место, и говорится: A single goroutine runs all finalizers for a program, sequentially. If a finalizer must run for a long time, it should do so by starting a new goroutine.
Ну вот бандиты же? Но могут себе позволить, горутины считай бесплатные, поэтому почему бы и нет, круто ↓
#дух_машины
BY Алло, это отладочная?

Share with your friend now:
tgoop.com/gdb_dbg/31