39

39


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

ВНИМАНИЕ :

1. Ваши объекты могут быть и не переданы сборщику мусора.

2. Сборка мусора не является удалением.

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

Для чего нужен метод finalize()?

Итак, если метод finalize() не стоит использовать для проведения стандартных операций завершения, то для чего же он нужен? Запомните третье правило:

ВНИМАНИЕ

3. Процесс сборки мусора относится только к памяти.

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

Но значит ли это, что если ваш объект содержит другие объекты, то в finalize() они должны явно удаляться? Нет — сборщик мусора займется ос¬вобождением памяти и удалением объектов вне зависимости от способа их соз¬дания. Получается, что использование метода finalize() ограничено особыми случаями, в которых ваш объект размещается в памяти необычным способом, не связанным с прямым созданием экземпляра. Но, если в Java все является объектом, как же тогда такие особые случаи происходят?

Похоже, что поддержка метода finalize() была введена в язык, чтобы сделать возможными операции с памятью в стиле С, с привлечением нестандартных ме¬ханизмов выделения памяти. Это может произойти в основном при использо¬вании методов, предоставляющих способ вызова He-Java-кода из программы на Java. С и С++ пока являются единственными поддерживаемыми языками, но, так как для них таких ограничений нет, в действительности программа Java может вызвать любую процедуру или функцию на любом языке. Во внешнем коде можно выделить память вызовом функций С, относящихся к семейству malloc(). Если не воспользоваться затем функцией free(), произойдет «утечка» памяти. Конечно, функция free() тоже принадлежит к С и С++, поэтому придет¬ся в методе finalize() провести вызов еще одного «внешнего» метода.

После прочтения этого абзаца у вас, скорее всего, сложилось мнение, что ме¬тод finalize() используется нечасто1. И правда, это не то место, где следует про¬водить рутинные операции очистки. Но где же тогда эти обычные операции бу¬дут уместны?

Очистка — ваш долг

Для очистки объекта его пользователю нужно вызвать соответствующий метод в той точке, где эти завершающие действия по откреплению и должны осущест¬вляться. Звучит просто, но немного протйворечит традиционным представле¬ниям о деструкторах С++. В этом языке все объекты должны уничтожаться. Ес¬ли объект С++ создается локально (то есть в стеке, что невозможно в Java), то удаление и вызов деструктора происходит у закрывающей фигурной скобки, ограничивающей область действия такого объекта. Если же объект создается оператором new (как в Java), то деструктор вызывается при выполнении про¬граммистом оператора С++ delete (не имеющего аналога в Java). А когда про¬граммист на С++ забывает вызвать оператор delete, деструктор не вызывается и происходит «утечка» памяти, к тому же остальные части объекта не проходят необходимой очистки. Такого рода ошибки очень сложно найти и устранить, и они являются веским доводом в пользу перехода с С++ на Java.

Java не позволяет создавать локальные объекты — все объекты должны быть результатом действия оператора new. Но в Java отсутствует аналог оператора delete, вызываемого для разрушения объекта, так как сборщик мусора и без того выполнит освобождение памяти. Значит, в несколько упрощенном изложении можно утверждать, что деструктор в Java отсутствует из-за присутствия сбор¬щика мусора. Но в процессе чтения книги вы еще не раз убедитесь, что наличие сборщика мусора не устраняет необходимости в деструкторах или их аналогах. (И никогда не стоит вызывать метод finalize() непосредственно, так как этот подход не решает проблему.) Если же потребуется провести какие-то завер¬шающие действия, отличные от освобождения памяти, все же придется явно вызвать подходящий метод, выполняющий функцию деструктора С++, но это уже не так удобно, как встроенный деструктор.

Помните, что ни сборка мусора, ни финализация не гарантированы. Если виртуальная машина Java (Java Virtual Machine, JVM) далека от критической точки расходования ресурсов, она не станет тратить время на освобождение па¬мяти с использованием сборки мусора.

Условие «готовности»

В общем, вы не должны полагаться на вызов метода finalize() — создавайте от¬дельные «функции очистки» и вызывайте их явно. Скорее всего, finalize() при¬годится только в особых ситуациях нестандартного освобождения памяти, с ко¬торыми большинство программистов никогда не сталкивается. Тем не менее существует очень интересное применение метода finalize(), не зависящее от того, вызывается ли он каждый раз или нет. Это проверка условия готовности объекта.

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

Report Page