Почему в java и С# есть финализаторы?

Я не совсем понимаю, почему есть финализаторы на таких языках, как java и С#. AFAIK, они:

  • не гарантируется выполнение (в java)
  • если они выполняются, они могут запускать произвольное количество времени после того, как объект, о котором идет речь, становится кандидатом на финализацию.
  • и (по крайней мере, в java), они понесут потрясающе огромную производительность, даже если она попала в класс.

Так почему они были добавлены вообще? Я спросил друга, и он что-то пробормотал: "Вы хотите иметь все возможности для очистки вещей, таких как соединения с DB", но это кажется мне плохой практикой. Почему вы должны полагаться на что-то с вышеописанными свойствами на что угодно, даже в качестве последней линии защиты? Особенно, когда, если что-то подобное было разработано в любом API, API API будет смеяться от существования.

Ответ 1

Ну, они невероятно полезны в определенных ситуациях.

В .NET CLR, например:

  • не гарантируется запуск

Финализатор всегда, в конце концов, запускается, если программа не будет убита. Он просто не детерминирован относительно того, когда он будет работать.

  • если они выполняются, они могут запускать произвольное количество времени после того, как объект, о котором идет речь, становится кандидатом на финализацию.

Это правда, однако они все еще работают.

В .NET это очень, очень полезно. Это довольно распространено в .NET для переноса собственных, не .NET-ресурсов в .NET-класс. Внедряя финализатор, вы можете гарантировать правильную очистку собственных ресурсов. Без этого пользователь будет вынужден вызвать метод для выполнения очистки, что значительно снижает эффективность сборщика мусора.

Не всегда легко точно узнать, когда выпустить свои (родные) ресурсы, выполнив финализатор, вы можете гарантировать, что они будут очищены правильно, даже если ваш класс используется менее совершенным образом.

  • и (по крайней мере, в java), они несут поразительно огромную производительность, даже если на класс

Опять же, GC.NET CLR имеет преимущество здесь. Если вы реализуете правильный интерфейс (IDisposable), И если разработчик правильно его реализует, вы можете предотвратить дорогостоящую часть завершения. То, как это делается, заключается в том, что пользовательский метод для очистки может вызывать GC.SuppressFinalize, который обходит финализатор.

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

Ответ 2

Hmya, вы рисуете здесь картину, которая слишком розовая. Финализаторы также не могут запускаться в .NET. Типичные неудачи - это финализатор, который генерирует исключение или тайм-аут в потоке финализатора (2 секунды).

Это была проблема, когда Microsoft решила предоставить поддержку хостинга .NET в SQL Server. Вид приложения, в котором перезапуск приложения для устранения утечек ресурсов не считается жизнеспособным обходным решением..NET 2.0 приобрел критические финализаторы, включенные с помощью класса CriticalFinalizerObject. Финализатор такого класса должен придерживаться правила областей ограниченного выполнения (ССВ), по существу, область кода, в которой исключения подавляются. То, что вы можете сделать в CER, очень ограничено.

Возвращаясь к исходному вопросу, финализаторы необходимы для освобождения ресурсов операционной системы, кроме памяти. Сборщик мусора очень хорошо управляет памятью, но не делает ничего, чтобы выпускать ручки, кисти, файлы, сокеты, окна, трубы и т.д. Когда объект использует такой ресурс, он должен обязательно освободить ресурс после его завершения Это. Финализаторы гарантируют, что это произойдет, даже когда программа забыла это сделать. Вы почти никогда не пишете класс с помощью финализатора самостоятельно, операционные ресурсы обертываются классами в рамках.

У платформы .NET также есть шаблон программирования, чтобы гарантировать, что такой ресурс будет выпущен раньше, чтобы ресурс не задерживался до завершения финализатора. Все классы, в которых есть финализаторы, также реализуют метод IDisposable.Dispose(), позволяя вашему коду выпустить ресурс явно. Это часто забывается программистом .NET, но это обычно не вызывает проблем, потому что финализатор гарантирует, что в конечном итоге это будет сделано. Многие программисты .NET потеряли много часов сна, беспокоясь о том, были ли затронуты все вызовы Dispose(), и на форумах началось огромное количество потоков. Java-люди должны быть счастливее.


В ответ на комментарий: исключения и таймауты в потоке финализатора - это то, о чем вам не о чем беспокоиться. Во-первых, если вы начинали писать финализатор, сделайте глубокий вдох и спросите себя, находитесь ли вы на правильном пути. Финализаторы для классов классов, вы должны использовать такой класс для использования рабочего ресурса, вы получите бесплатный финализатор, встроенный в этот класс. На всем пути к классам SafeHandle у них есть критический финализатор.

Во-вторых, сбои нитей финализатора являются грубыми ошибками программы. Подобно тому, как исключение OutOfMemory исключает или отключает шнур питания и отключает устройство. Вы ничего не можете с ними поделать, кроме исправления ошибки в коде или перенаправления кабеля. Для Microsoft было важно разработать критически важные финализаторы, они не могут полагаться на всех программистов, которые пишут .NET-код для SQL Server, чтобы правильно получить этот код. Если вы сами перепутаете финализатор, тогда нет такой ответственности, вы получите звонок от клиента, а не от Microsoft.

Ответ 3

Если вы читаете JavaDoc для finalize(), он говорит: "Вызывается сборщиком мусора на объекте, когда сбор мусора определяет, что больше нет ссылок на объект. Подкласс переопределяет метод finalize для утилизации системных ресурсов или выполнить другую очистку".

http://java.sun.com/javase/6/docs/api/java/lang/Object.html#finalize

Так что "почему". Я думаю, вы можете утверждать, эффективна ли их реализация.

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

class MyResource {
    private Throwable allocatorStack;

    public MyResource() {
        allocatorStack = new RuntimeException("trace to allocator");
    }

    @Override
    protected void finalize() throws Throwable {
        try {
            System.out.println("Bug!");
            allocatorStack.printStackTrace();
        } finally {
            super.finalize();
        }
    }
 }

Ответ 4

В java финализаторы существуют, чтобы разрешить очистку внешних ресурсов (вещи, которые существуют за пределами JVM и не могут быть собраны в мусор, когда "родительский" Java-объект). Это всегда было редко. Например, может быть, если вы взаимодействуете с каким-то специальным оборудованием.

Я думаю, что причина, по которой финализаторы в java не гарантируются, заключается в том, что у них может не быть возможности сделать это при завершении программы.

Одна вещь, которую вы можете сделать с финализатором в "чистой" java, - использовать ее для проверки условий завершения - например, чтобы проверить, закрыты ли все соединения и сообщить об ошибке, если они не являются. Вам не гарантируется, что ошибка будет всегда поймана, но она, скорее всего, будет поймана по крайней мере в течение некоторого времени, достаточного для выявления ошибки.

В большинстве Java-кодов нет приглашений для финализаторов.

Ответ 5

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

Конечно, во многих случаях как автор приложения вы узнаете, что существует только одна ссылка на соединение с БД (например); в этом случае финализаторы не могут заменить его правильно, когда вы знаете, что закончили с ним.

Ответ 6

В .Net земля t не гарантируется , когда они запускаются. Но они будут работать.

Ответ 7

Вы ссылаетесь на Object.Finalize?

Согласно msdn, "В коде С# Object.Finalize нельзя вызвать или переопределить". Фактически, они рекомендуют использовать метод Dispose, потому что он более управляем.

Ответ 8

В .NET есть дополнительное осложнение с финализаторами. Если класс имеет финализатор и не получает Dispose() 'd, или Dispose() не подавляет финализатор, сборщик мусора откладывает сбор до тех пор, пока не будет сгенерирована память второго поколения (последнее поколение), поэтому объект "сортируется" "но не совсем утечки памяти". (Да, в конце концов, он будет очищен, но вполне возможно, что до завершения приложения.)

Как уже упоминалось, если объект имеет не управляемые ресурсы, он должен реализовать шаблон IDisposable. Разработчики должны знать, что если объект реализует IDisposable, тогда всегда должен вызываться метод Dispose(). С# предоставляет возможность автоматизировать это с помощью инструкции using:

using (myDataContext myDC = new myDataContext())
{
    // code using the data context here
}

Блок использования автоматически вызывает Dispose() при выходе из блока, даже завершает возврат или исключение. Оператор using работает только с объектами, реализующими IDisposable.

И остерегайтесь еще одной путаницы; Dispose() - это возможность для объекта освобождать ресурсы, но не фактически освобождает объект Dispose() 'd. Объекты .NET являются интеллектуальными для сбора мусора, когда нет активных ссылок на них. Технически они не могут быть достигнуты ни одной цепочкой ссылок на объекты, начиная с AppDomain.

Ответ 9

Эквивалент destructor() в С++ - это finalizer() в Java.

Они вызывают, когда жизненный цикл объекта близок к завершению.