Главная » Java » Finalize в Java

0

 

Вообще говоря, мы не в состоянии заметить, когда именно происходит фактическое удаление "осиротевших" объектов, — процесс, что называется, просто "идет". Но в составе любого класса может быть реализован метод finalize, который вызывается сборщиком мусора непосредственно перед удалением объекта класса. Метод предоставляет программисту шанс привести объект в "чистое" состояние, освободив все используемые им ресурсы, не относящиеся к памяти. Метод finalize объявлен в классе Object.

protected void finalize()  throws Throwable

Вызывается процессом сборки мусора, когда тот обнаруживает, что объект больше недостижим и должен быть удален. Конкретная реализация метода может использоваться для высвобождения используемых объектом ресурсов, не имеющих отношения к памяти. Для конкретного объекта метод активизируется, самое большее, один раз — даже в том случае, если в процессе выполнения программы объект несколько раз меняет статус достижимости. Гарантий, что метод finalize будет выполнен в строго определенный момент времени, однако, не существует — он может не вызываться исполняющей системой вовсе. Метод способен выбрасывать любые объявляемые исключения, но если это происходит, объект исключения сборщиком мусора игнорируется. Виртуальная машина не может поручиться, каким именно потоком вычислений будет вызван метод finalize любого определенного объекта, но что она все-таки способна гарантировать, так это отсутствие блокировки объекта потоком.

   Потребность в реализации метода finalize возникает относительно редко, но если вы беретесь за эту работу, то должны выполнить ее с особой тщательностью. Если объект переходит в категорию мусора, вполне возможно, что в нее же угодят и другие объекты, на который он ссылается. Будучи мусором, они могут быть утилизированы с вызовом соответствующих методов finalize прежде, чем выполнится ваш собственный finalize, и поэтому, не исключено, приобретут не то состояние, какое вы ожидали.

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

  Тем не менее объекты, участвующие в распределении внешних ресурсов, вполне могут использовать соответствующие реализации finalize, позволяющие привести состояние объектов в порядок и избежать опасности утечки ресурсов. Например, класс, занимающийся файловыми операциями, должен содержать в своем составе некоторую разновидность метода close, предусматривающего принудительное закрытие файла, — это даст возможность программисту, применяющему класс, непосредственно управлять ресурсом одновременно открытых файлов. Вызов close целесообразно включить в тело метода finalize, хотя, разумеется, для предотвращения возможных проблем одной этой меры явно не достаточно. Возможно, пользователям вашего кода повезет и метод fialize будет выполнен до момента истощения ресурса открытых файлов, но полагаться на подобную случайность нельзя: finalize — это средство из разряда последних "решительных" мер, позволяющих избежать неприятностей, если программист не удосужился предпринять аналогичные действия заблаговременно. Рассмотрим пример.

public class  processFile  {

     private  FileReader file;

 

public  ProcessFile(String path)  throws

   FileNotFoundException

{

   file = new FileReader(path);

}

// ….

 

public synchronized void close( )  throws  lOException  {

 if (file   != null)   {

 file.close( );

 file = null;

  }

}

protected void finalizeO  throws Throwable {

 try {

   close( );

   }  finally {

     super.finalize( );

  }

 }

}

Метод close обязан корректно обрабатывать ситуацию многократного вызова. В противном случае, если обращение к нему выполняется еще до момента выполнения метода finalize, последний не должен вызывать close еще раз — попытка повторного закрытия файла явно ошибочна.

  Обратите внимание и на то, как в предложении finally посредством ссылки super вызывается finalize базового класса. Приучите себя к необходимости поступать так при написании любого варианта метода finalize. В отсутствие вызова super. finalize вы сможете корректно очистить только состояние своего собственного, "расширенного", фрагмента объекта, но его часть, относящаяся к ведению базового класса, так и не будет обработана должным образом. Обращение к super, finalize можно считать одной из полезных привычек, которую следует культивировать, и применение такой инструкции оправданно даже в тех случаях, когда класс не относится к расширенным, — теперь вы всегда сможете изменить реализацию класса, добавив в его объявление упоминание о базовом классе, и вам не придется заботиться о необходимости просмотра и изменения кода метода finalize. Вызов finalize базового класса, осуществляемый в контексте предложения finally, — это гарантия того, что процесс очистки состояния экземпляра базового класса будет выполнен даже в том случае, если в ходе выполнения метода текущего объекта выбрасывается исключение.

   Сборщик мусора может удалять объекты в любом порядке либо не удалять их вовсе. Ресурсы памяти восстанавливаются тогда, когда, по "мнению" сборщика мусора, наступает подходящий момент. Не будучи связанным никакими жесткими обязательствами относительно расписаний своей работы, сборщик выбирает стратегию, которую считает наиболее эффективной и способствующей максимальному снижению издержек. Программист вправе, если это необходимо, попытаться принудительно спровоцировать выполнение сборки мусора с помощью методов System.gc и Runtime.gc (мы расскажем о них в разделе 12.4), но система не способна поручиться, что процесс произойдет в действительности.

   Когда приложение завершает работу, никакая специальная процедура сборки мусора не выполняется, так что для объектов, не утилизированных ранее, методы finalize так и не будут вызваны. В большинстве случаев это не приведет к осложнениям, поскольку по окончании работы виртуальные машины, как правило, автоматически закрывают все ранее открытые файлы и сетевые соединения. (Впрочем, временные файлы в момент создания могут быть помечены признаком "удалять при выходе", что обеспечивает простое решение одной из неприятных проблем, — за подробной информацией обратитесь к разделу 15.6.3 на странице 412.) Но если приложение работает с ресурсами другого вида, не относящимися к системным, вам придется предлагать иные решения.

 

Источник: Арнолд, Кен, Гослинг, Джеймс, Холмс, Дэвид. Язык программирования Java. 3-е изд .. : Пер. с англ. – М. : Издательский дом «Вильяме», 2001. – 624 с. : ил. – Парал. тит. англ.

По теме:

  • Комментарии