Есть ли деструктор в Java?
Заголовок: Есть ли деструктор в Java?
Описание проблемы:
Здравствуйте!
Я столкнулся с проблемой в Java и не могу найти соответствующую документацию. Вопрос в том, существует ли в Java деструктор? Если его нет, то как я могу добиться аналогичного эффекта?
Чтобы сделать мой вопрос более конкретным, я разрабатываю приложение, которое работает с данными, и в спецификации указано, что должен быть 'кнопка сброса', которая возвращает приложение в его исходное состояние после запуска. Однако все данные должны оставаться 'активными', если приложение не закрыто или не нажата кнопка сброса.
Будучи, как правило, программистом на C/C++, я думал, что это будет тривиально реализовать (поэтому и планировал заняться этой частью последним). Я структурировал свою программу так, что все 'сбрасываемые' объекты находятся в одном классе, чтобы я мог просто уничтожить все 'активные' объекты, когда нажимается кнопка сброса.
Я задумывался, если я просто разыменую данные и буду ждать, пока сборщик мусора их соберет, не произойдет ли утечка памяти, если пользователь будет repeatedly вводить данные и нажимать кнопку сброса? Я также надеялся, что поскольку Java — довольно зрелый язык, существует способ предотвратить это или справиться с этой ситуацией более изящно.
Буду признателен за любые советы или рекомендации!
5 ответ(ов)
Нет, деструкторов здесь нет. Причина в том, что все объекты в Java выделяются в куче и собираются сборщиком мусора. Без явного освобождения памяти (то есть оператора delete в C++) нет разумного способа реализовать настоящие деструкторы.
Java поддерживает финализаторы, но их следует использовать только как предосторожность для объектов, удерживающих дескрипторы нативных ресурсов, таких как сокеты, дескрипторы файлов, дескрипторы окон и т. д. Когда сборщик мусора обрабатывает объект без финализатора, он просто помечает область памяти как свободную, и на этом всё. Когда у объекта есть финализатор, он сначала копируется во временное место (не забудьте, что мы занимаемся сборкой мусора), затем помещается в очередь ожидания финализации, и поток финализатора с очень низким приоритетом опрашивает эту очередь и выполняет финализатор.
Когда приложение завершает свою работу, JVM останавливается, не дожидаясь завершения финализации ожидающих объектов, так что практически нет гарантии, что ваши финализаторы когда-либо будут выполнены.
Если вас беспокоит только память, не стоит переживать. Просто доверьтесь сборщику мусора, он справляется с работой неплохо. Я даже где-то видел, что в некоторых случаях эффективность работы сборщика мусора может быть такой высокой, что создание множества мелких объектов может быть лучше для производительности, чем использование больших массивов.
С выходом Java 1.7 у вас появилась дополнительная возможность использовать блок try-with-resources
. Например:
public class Closeable implements AutoCloseable {
@Override
public void close() {
System.out.println("closing...");
}
public static void main(String[] args) {
try (Closeable c = new Closeable()) {
System.out.println("trying...");
throw new Exception("throwing...");
}
catch (Exception e) {
System.out.println("catching...");
}
finally {
System.out.println("finalizing...");
}
}
}
Если вы выполните этот класс, метод c.close()
будет вызван, когда блок try
будет завершен, и перед выполнением блоков catch
и finally
. В отличие от метода finalize()
, выполнение метода close()
гарантировано. Однако нет необходимости вызывать его явно в блоке finally
.
Сначала стоит отметить, что в Java используется сборка мусора, поэтому необходимость вручную управлять уничтожением объектов возникает довольно редко. Во-первых, вам обычно не нужно освобождать управляемые ресурсы, а во-вторых, вы не можете предсказать, когда именно произойдет сборка мусора, поэтому использовать этот механизм для объектов, которые должны быть очищены "как только кто-то перестанет их использовать", неуместно.
Для получения уведомления о том, что объект был уничтожен, можно использовать java.lang.ref.PhantomReference
(хотя утверждение о том, что объект уничтожен, может быть несколько неточным, поскольку если на него есть фантомная ссылка, это означает, что он больше не может быть восстановлен, что, как правило, эквивалентно уничтожению). Вот типичный подход:
- Выделите ресурсы вашего класса, которые необходимо освободить, в отдельный вспомогательный объект (заметьте, что если вы просто закрываете соединение, что является распространенным случаем, то вам не нужно создавать новый класс: закрываемое соединение может быть "вспомогательным объектом" в этом случае).
- При создании основного объекта создайте также фантомную ссылку на него. Эта ссылка может указывать на новый вспомогательный объект или же вы можете организовать отображение от фантомных ссылок к соответствующим вспомогательным объектам.
- После того как основной объект будет собран, фантомная ссылка будет добавлена в очередь (или, точнее, может быть добавлена – как и в случае с финализаторами, нет гарантии, что это произойдет, например, если виртуальная машина завершит работу, она не будет ждать). Убедитесь, что вы обрабатываете эту очередь (либо в отдельном потоке, либо периодически). Поскольку существует жесткая ссылка на вспомогательный объект, последний еще не был собран. Поэтому выполните любые необходимые операции по очистке на вспомогательном объекте, после чего удавите фантомную ссылку, и вспомогательный объект в конечном итоге также будет собран.
Также есть метод finalize()
, который выглядит как деструктор, но на самом деле работает иначе. Обычно его не стоит использовать.
Функция finalize()
является деструктором.
Однако её не следует использовать, так как она вызывается после сборки мусора (GC), и вы не можете заранее знать, когда это произойдет (если произойдет вообще).
Кроме того, для освобождения объектов с finalize()
может потребоваться больше одной сборки мусора.
Рекомендуется производить очистку в логических местах вашего кода с использованием блоков try{...} finally{...}
!
Ошибка java.lang.OutOfMemoryError: превышен лимит времени работы сборщика мусора
Что значит "Не удалось найти или загрузить основной класс"?
Как установить Java 8 на Mac
Почему в RecyclerView отсутствует onItemClickListener()?
Что значит 'synchronized'?