На нашем сайте мы используем cookie для сбора информации технического характера и обрабатываем IP-адрес вашего местоположения. Продолжая использовать этот сайт, вы даете согласие на использование файлов cookies. Здесь вы можете узнать, как мы пользуемся файлами cookies.
Я согласен
логотип upread.ru

Урок 18. Сборщик мусора и метод finalize в Java


В этой статье из моего бесплатного курса Java я буду обсуждать метод Object finalize() в Java. Объект class, который является суперклассом всех классов, определяет метод finalize(), а также другие методы, включая clone(), toString(), hashCode() и equals().

Что такое finalize?

finalize() - это хук метод объекта класса.

Метод хук - это пустой метод в базовом классе, который можно переопределить, чтобы обеспечить новую или расширенную функциональность, которая будет вызываться в определенных ситуациях существующим процессом. Другими словами, у вас есть механизм, чтобы “зацепиться” за существующий процесс и расширить его функциональность.

Эта концепция существует независимо от какого-либо конкретного языка программирования. Используя наследование, это может быть реализовано в Java путем предоставления пустого метода в родительском классе. При перезаписи этого метода в дочернем классе предоставленный код будет автоматически вызван окружающей логикой/процессом.

Согласно документации Java, он вызывается сборщиком мусора, когда больше нет ссылок на объект. Обычная цель этого метода - выполнить действия по очистке непосредственно перед тем, как объект будет безвозвратно отброшен.

Сборщик мусора

Сборщик мусора, как вы можете себе представить, собирает "мусор" в вашей программе. Мусор, который очищается, - это объекты, которые вы создали, обработали, а затем отложили в сторону, когда они вам больше не нужны. Главная обязанность сборщика мусора - освободить ресурсы памяти, чтобы ваша программа, как мы надеемся, никогда не заканчивалась. Однако сборщик мусора работает асинхронно, и мы не имеем никакого контроля или влияния на то, будет ли он работать и когда. Мы можем дать ему рекомендации, но это всего лишь рекомендации. Сборщик мусора не связан ими.

Компьютер использует несколько типов аппаратных ресурсов, и они физически ограничены природой. Поэтому мы должны разумно использовать эти ресурсы. В Java, в отличие от более старых языков программирования, таких как C++, память автоматически управляется сборщиком мусора; однако существуют и другие ресурсы, обычно “ресурсы ввода-вывода”, которыми мы должны управлять самостоятельно.

“I” или “IO” входящие (ввод) данные, и “O” – вывод. Таким образом, IO-это общий термин для функциональности ввода или вывода (также называемой коммуникацией) из такого ресурса, как файл, внешняя система или база данных. Входные данные считываются в нашу систему, а выходные данные записываются из нашей системы. Типичное имя класса или интерфейса, используемого для чтения входных данных, - “Reader”, в то время как типичное имя класса или читателя, используемого для записи выходных данных, - “Writer”.

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

Если вам интересно узнать больше об этом, вы можете прочитать эффективную Java Джошуа Блоха, где он провел много интересных исследований на эту тему. Например, он проверил и обнаружил, что создание объекта и его уничтожение с помощью переопределенного метода finalize() происходит в 430 раз медленнее, чем с помощью унаследованного метода.

Переопределение Finalize()

Чтобы запустить метод finalize(), давайте реализуем класс Porsche, в котором мы его переопределим. Чтобы переопределить метод, мы должны убедиться, что имя и сигнатура переопределенного метода точно совпадают как в суперклассе, так и в подклассе. Единственное изменение, которое я собираюсь сделать, и это разрешено, заключается в том, что я собираюсь сделать его публичным, потому что публичность более заметна, чем защищена. Это позволяет метод, который должен вызываться за пределами нашего класса Порше.

public class Porsche {

     public void finalize(){

     }

}
Аннотация @Override

Аннотация @Override помогает нам быть уверенными, что мы переопределяем метод, а не просто случайно пишем новый метод, поскольку вы можете переопределить метод только в том случае, если сигнатура метода такая же, как и в суперклассе (сигнатура метода-это его имя, а также номер и тип его параметров). Если мы добавим аргумент к нашему методу finalize (), это уже не будет переопределением, а перегрузкой метода. (Это просто метод, который имеет то же имя, но с другими параметрами), и это даст нам ошибку времени компиляции.

Это помогает нам сразу же определить во время компиляции, еще находясь в нашей IDE, проблему, которая может привести к неожиданным результатам во время выполнения (например, наша “переопределенная” функция не вызывается). Вот почему всякий раз, когда вы переопределяете метод, вы всегда должны добавлять аннотацию @override.

public class Porsche {

     IOReader ioReader = new IOReader();

     @Override
     public void finalize(){
          ioReader.close();
     }
}
Внутри нашего метода finalize я написал код, который вызывает метод close() для всех объектов, которые создает объект класса Porsche. Предположим, например, что у нас есть IOReader, который читает поток символов из файла. Как только мы закончим, мы хотели бы закрыть этот ридер, вызвав метод close(), предоставленный IOReader. Теперь у нас есть наш перезаписанный метод finalize ().

Однако есть проблемы с нашей реализацией. Во-первых, как уже упоминалось, у нас нет никакой гарантии, что этот метод когда-либо будет вызван. Он полностью находится под контролем СПМ и вне нашего влияния. Вторая проблема заключается в том, что если в этом коде у нас есть какое-либо исключение, которое не обрабатывается, процесс остановится, и объекты останутся в странном “зомби” состоянии, которое замедляет сборку мусора.

Альтернатива финализации

Рекомендуемой альтернативой было бы создать наш собственный метод close(), который очищает/закрывает все ресурсы, которые больше не используются, в данном случае IoReader. Таким образом, мы имеем больший контроль над нашими ресурсами и не зависим от сборщика мусора.

public class Porsche {

     IOReader ioReader = new IOReader();

     public void close(){
          ioReader.close();
     }
}
Давайте также напишем черновик закрытия объекта в классе CarSelector. Во-первых, давайте создадим класс CarSelector и добавим объект Porsche со следующей строкой: Porsche porsche = new Porsche(); и используем наш новый метод close(). Мы окружим try, catch, finally заблокируем и используем наш последний блок, чтобы очистить наш Porsche. Критическим моментом здесь является то, что у вас есть раздел finally в конце, потому что самое классное в finally заключается в том, что даже если возникает исключение, оно гарантированно всегда будет выполнено. Это именно то, что нам нужно, чтобы решить нашу проблему и убедиться, что все ресурсы, не связанные с памятью, всегда свободны. Вот как вы можете правильно сделать это на Java:

public class CarSelector {
 
     public static void main(String[] arguments) {
          Porsche porsche = new Porsche();
 
          try {
               // some code
          } finally {
               porsche.close();
          }
     }
}
Таким образом, мы гарантируем, что, как только мы закончим с нашим объектом porsche, мы закроем все связанные с ним ресурсы, не перезаписывая finalize(). Блок finally делает именно то, что мы хотели, чтобы наш метод finalize() сделал.

Метод finalize() крайне несовершенен. У сборщика мусора есть собственный разум, и мы не можем по-настоящему контролировать, когда и как он работает. Поскольку переопределение метода finalize() эффективно не решает нашу проблему, вы можете вместо этого использовать блок try-(catch)-finally, чтобы закрыть любые дополнительные ресурсы, когда вы закончите с объектом. Поскольку метод close - это наш собственный метод, не связанный со сборщиком мусора, он работает точно так, как задумано, каждый раз.



Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.



тегистатьи IT, уроки по java, java, сборщик мусора, финализация





Отправляя сообщение я подтверждаю, что ознакомлен и согласен с политикой конфиденциальности данного сайта.




Где искать задачи начинающим программистам
Урок 9. Ожидание завершения параллельных задач C#


© upread.ru 2013-2020
При перепечатке активная ссылка на сайт обязательна.
Задать вопрос
письмо
Здравствуйте! Вы можете задать мне любой вопрос. Если не получается отправить сообщение через эту форму, то пишите на почу up777up@yandex.ru
Отправляя сообщение я подтверждаю, что ознакомлен и согласен с политикой конфиденциальности данного сайта.