Есть ли деструктор для Java? Кажется, я не могу найти никакой документации по этому поводу. Если нет, как я могу добиться такого же эффекта?
Чтобы сделать мой вопрос более конкретным, я пишу приложение, которое имеет дело с данными, и в спецификации говорится, что должна быть кнопка «сброса», которая возвращает приложение в исходное, только что запущенное состояние. Однако все данные должны быть «живыми», если приложение не закрыто или не нажата кнопка сброса.
Как обычно, программист на C / C++, я подумал, что реализовать это будет тривиально. (И поэтому я планировал реализовать это последним.) Я структурировал свою программу таким образом, чтобы все «сбрасываемые» объекты находились в одном классе, чтобы я мог просто уничтожить все «живые» объекты при нажатии кнопки сброса.
Я подумал, что если бы все, что я делал, было просто разыменовать данные и ждать, пока сборщик мусора их соберет, не возникнет ли утечка памяти, если бы мой пользователь несколько раз вводил данные и нажимал кнопку сброса? Я также думал, что, поскольку Java достаточно зрелый язык, должен быть способ предотвратить это или изящно решить эту проблему.
Виртуальная машина не будет запускать сборщик мусора достаточно скоро, если вы быстро обрабатываете данные через объекты. Идея о том, что GC всегда может идти в ногу или принимать правильные решения, является ошибкой.
@Kieveli Разве JVM не запускает сборщик мусора, прежде чем выдаст ошибку?
Да, было бы неплохо, если бы для Java был деструктор, который уничтожил бы его раз и навсегда.




Нет, java.lang.Object#finalize - самое близкое из возможных.
Однако, когда (и если) он вызывается, не гарантируется.
См .: java.lang.Runtime#runFinalizersOnExit(boolean)
Метод, который может или не может быть вызван, в моей книге по сути бесполезен. Было бы лучше не загрязнять язык бесполезным специальным методом, который в лучшем случае дает ложное чувство безопасности. Я никогда не пойму, почему разработчики языка Java посчитали, что финализация - хорошая идея.
@antred Разработчики языка Java соглашаются. Думаю, тогда для некоторых из них это был первый раз, когда они разработали язык программирования и среду выполнения со сборкой мусора. Что менее понятно, так это то, почему этот другой управляемый язык скопировал эту концепцию в то время, когда уже было понято, что эта концепция - плохая идея.
Функция finalize() является деструктором.
Однако его обычно не следует использовать, потому что он вызывается после GC, и вы не можете сказать, когда это произойдет (если когда-либо).
Более того, для освобождения объектов с finalize() требуется более одного сборщика мусора.
Вы должны попытаться очистить логические места в вашем коде с помощью операторов try{...} finally{...}!
Поскольку Java - это язык со сборкой мусора, вы не можете предсказать, когда (или даже если) объект будет уничтожен. Следовательно, нет прямого эквивалента деструктора.
Существует унаследованный метод finalize, но он вызывается полностью по усмотрению сборщика мусора. Итак, для классов, которые необходимо явно привести в порядок, соглашение состоит в том, чтобы определить метод Закрыть и использовать finalize только для проверки работоспособности (т.е. если Закрыть не был вызван, сделайте это сейчас и запишите ошибку).
Недавно был вопрос, который вызвал всестороннее обсуждение финализации, так что при необходимости он должен обеспечить большую глубину ...
Обращается ли «close ()» в этом контексте к методу в java.lang.Autocloseable?
Нет, AutoCloseable был введен в Java 7, но соглашение close () существует гораздо дольше.
почему вы не можете предсказать, когда (или даже если) объект будет уничтожен. какой примерный другой способ предсказать это?
Уничтожение объекта @dctremblay выполняется сборщиком мусора, и сборщик мусора может никогда не запускаться за время существования приложения.
Обратите внимание, что метод finalizeустарел в Java 9.
Мы также можем использовать WeakReference - объекты, хранящиеся там, будут удалены до того, как сборщик мусора начнет поиск мусора.
@dctremblay разрушение объектов - это не вещь. В какой-то момент память неиспользуемого объекта может быть перезаписана, когда она используется для другого объекта. Вся работа, связанная со сборкой мусора, будет отложена до тех пор, пока память действительно не понадобится для другого объекта. Этого может быть никогда, если а) нет последующих выделений или б) памяти достаточно для всех последующих выделений. Даже когда сборщик мусора работает, он обычно не касается мусора. Его основная задача - гарантировать, что все еще используемые объекты не будут перезаписаны. Он не идентифицирует мертвые объекты.
Ближайшим эквивалентом деструктора в Java является метод финализировать (). Большое отличие от традиционного деструктора заключается в том, что вы не можете быть уверены, когда он будет вызван, поскольку за это отвечает сборщик мусора. Я настоятельно рекомендую внимательно прочитать это, прежде чем использовать его, поскольку ваши типичные шаблоны RAIA для дескрипторов файлов и т. д. Не будут надежно работать с finalize ().
Нет, деструкторов здесь нет. Причина в том, что все объекты Java выделяются в куче и собираются мусором. Без явного освобождения (т.е. оператора удаления в C++) нет разумного способа реализовать реальные деструкторы.
Java поддерживает финализаторы, но они предназначены для использования только в качестве защиты для объектов, содержащих дескриптор собственных ресурсов, таких как сокеты, дескрипторы файлов, дескрипторы окон и т. д. Когда сборщик мусора собирает объект без финализатора, он просто помечает память регион как свободный и все тут. Когда у объекта есть финализатор, он сначала копируется во временное место (помните, мы здесь собираем мусор), затем он помещается в очередь ожидания финализации, а затем поток финализатора опрашивает очередь с очень низким приоритетом. и запускает финализатор.
Когда приложение завершает работу, JVM останавливается, не дожидаясь завершения работы над ожидающими объектами, поэтому практически нет никаких гарантий, что ваши финализаторы когда-либо будут запущены.
Спасибо за упоминание собственных ресурсов - это одна из областей, где полезен «деструкторный» метод.
да, прямо сейчас я сталкиваюсь с той же проблемой с освобождением ресурсов / дескрипторов, выделенных через собственные вызовы C++.
@ddimitrov, теоретически может ли Java реализовать явное освобождение? Или это логическое противоречие?
@mils, наивная реализация явного освобождения, либо нарушит предположение Java, что любая ссылка указывает на живой объект. Вы можете перебирать все указатели и обнулять псевдонимы, но это дороже, чем сборщик мусора. Или вы можете попробовать использовать какую-нибудь систему линейных типов (см. «Владение» в Rust), но это серьезное изменение языка. Есть и другие варианты (см. Память с областью видимости JavaRT и т. д.), Но в целом явное освобождение не соответствует языку Java.
Следует избегать использования методов финализировать (). Они не являются надежным механизмом очистки ресурсов, и злоупотребление ими может вызвать проблемы в сборщике мусора.
Если вам требуется вызов освобождения вашего объекта, например, для освобождения ресурсов, используйте явный вызов метода. Это соглашение можно увидеть в существующих API (например, Закрывающийся, Graphics.dispose (), Widget.dispose ()) и обычно вызывается через try / finally.
Resource r = new Resource();
try {
//work
} finally {
r.dispose();
}
Попытки использовать удаленный объект должны вызывать исключение времени выполнения (см. IllegalStateException).
Обновлено:
I was thinking, if all I did was just to dereference the data and wait for the garbage collector to collect them, wouldn't there be a memory leak if my user repeatedly entered data and pressed the reset button?
Как правило, все, что вам нужно сделать, это разыменовать объекты - по крайней мере, так это должно работать. Если вас беспокоит сборка мусора, ознакомьтесь с Java SE 6 HotSpot [tm] Настройка сборки мусора виртуальной машины (или аналогичным документом для вашей версии JVM).
Это не то, что означает разыменование. Он не «устанавливает для последней ссылки объекта значение null», а скорее получает (считывает) значение из ссылки, чтобы вы могли использовать его для последующих операций.
Попытка .. наконец-то все еще действующий и рекомендуемый подход? Предположим, я ранее вызывал собственный метод в finalize (), могу ли я переместить вызов в предложение finally? class Resource { finalize() { destroy(); } protected native void destroy(); } class Alt_Resource { try (Resource r = new Resource()) { // use r } finalize { r.destroy(); }
r не будет ограничен блоком finally. Следовательно, на этом этапе вы не можете вызывать уничтожение. Теперь, если вы исправите область видимости, чтобы объект был создан до блока try, вы получите уродливый случай «до попытки с ресурсами».
Возможно, вы можете использовать блок try ... finally для завершения объекта в потоке управления, в котором вы используете этот объект. Конечно, это не происходит автоматически, но и разрушение не происходит в C++. Вы часто видите закрытие ресурсов в блоке finally.
Это правильный ответ, когда у рассматриваемого ресурса есть единственный владелец и никогда не бывает ссылок на него, «украденных» другим кодом.
Я полностью согласен с другими ответами, говоря, что не следует полагаться на выполнение finalize.
В дополнение к блокам try-catch-finally вы можете использовать Время выполнения # addShutdownHook (представленный в Java 1.3) для выполнения окончательной очистки в вашей программе.
Это не то же самое, что деструкторы, но можно реализовать ловушку выключения, имеющую зарегистрированные объекты слушателя, на которых могут быть вызваны методы очистки (закрытие постоянных соединений с базой данных, снятие блокировки файлов и т. д.) - вещи, которые обычно выполняется в деструкторах. Опять же - это не замена деструкторам, но в некоторых случаях вы можете достичь желаемой функциональности с помощью этого.
Преимущество этого заключается в том, что у остальной части вашей программы есть поведение деконструкции слабо связанный.
addShutdownHook был явно представлен в Java 1.3. Во всяком случае, он доступен мне в 1.5. :) Смотрите это: stackoverflow.com/questions/727151/…
К вашему сведению, по моему опыту, хуки выключения не будут вызываться, если вы используете красную кнопку «завершить» в Eclipse - вся JVM немедленно уничтожается, хуки выключения не вызываются изящно. Это означает, что вы можете увидеть другое поведение во время разработки и производства, если вы разрабатываете с использованием eclipse.
Во-первых, обратите внимание, что, поскольку Java собирает мусор, редко нужно что-либо делать для уничтожения объекта. Во-первых, потому что у вас обычно нет управляемых ресурсов для освобождения, а во-вторых, потому что вы не можете предсказать, когда или если это произойдет, поэтому это неуместно для вещей, которые вам нужно произойти, «как только никто больше не будет использовать мой объект. ".
Вы можете получить уведомление после того, как объект был уничтожен с помощью java.lang.ref.PhantomReference (на самом деле, сообщение о том, что объект был уничтожен, может быть немного неточным, но если фантомная ссылка на него поставлена в очередь, то он больше не подлежит восстановлению, что обычно составляет тоже самое). Обычное использование:
Также есть finalize (), который выглядит как деструктор, но не ведет себя как деструктор. Обычно это не лучший вариант.
Почему PhantomReference вместо WeakReference?
@uckelman: если все, что вам нужно, это уведомление, то PhantomReference выполняет свою работу, это в значительной степени то, для чего он предназначен. Дополнительная семантика WeakReference здесь не нужна, и в тот момент, когда ваш ReferenceQueue уведомлен, вы больше не можете восстановить объект через WeakReference, поэтому единственная причина для его использования - сохранить необходимость помнить о существовании PhantomReference. Любая дополнительная работа, которую выполняет WeakReference, вероятно, незначительна, но зачем с ней возиться?
Спасибо, что намекнули на PhantomReference. Это не идеально, но все же лучше, чем ничего.
@SteveJessop, какая «дополнительная работа», по вашему мнению, имеет слабую ссылку по сравнению с фантомной ссылкой?
Если вас беспокоит просто воспоминание, не надо. Просто поверьте, GC он делает достойную работу. Я действительно видел кое-что в том, что это настолько эффективно, что для производительности может быть лучше создавать кучи крошечных объектов, чем в некоторых случаях использовать большие массивы.
Хотя в технологии Java GC были достигнуты значительные успехи, вам все же нужно помнить о своих ссылках. На ум приходят многочисленные случаи, казалось бы, тривиальных эталонных шаблонов, которые на самом деле представляют собой крысиные гнезда под капотом.
Из вашего сообщения не похоже, что вы пытаетесь реализовать метод сброса с целью повторного использования объекта (правда?). Содержат ли ваши объекты какие-либо другие типы ресурсов, которые необходимо очистить (например, потоки, которые должны быть закрыты, любые объединенные или заимствованные объекты, которые необходимо вернуть)? Если вас беспокоит только освобождение памяти, я бы пересмотрел свою структуру объекта и попытался проверить, что мои объекты являются автономными структурами, которые будут очищены во время сборки мусора.
Если вы пишете Java-апплет, вы можете переопределить метод «destroy ()» апплета. Это...
* Called by the browser or applet viewer to inform * this applet that it is being reclaimed and that it should destroy * any resources that it has allocated. The stop() method * will always be called before destroy().
Очевидно, это не то, что хочет ты, но может быть то, что ищут другие люди.
Взгляните на оператор попробовать с ресурсами. Например:
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
System.out.println(br.readLine());
} catch (Exception e) {
...
} finally {
...
}
Здесь ресурс, который больше не нужен, освобождается методом BufferedReader.close(). Вы можете создать свой собственный класс, реализующий AutoCloseable, и использовать его аналогичным образом.
Этот оператор более ограничен, чем finalize, с точки зрения структурирования кода, но в то же время он упрощает понимание и сопровождение кода. Кроме того, нет гарантии, что метод finalize будет вызван вообще во время работы приложения.
Я удивлен, что у него так мало голосов. Это настоящий ответ.
Однако зависит от Java 7, и на Java 6 все еще есть множество систем (и несколько сотен миллионов устройств Android, ни одно из которых не имеет официальной поддержки Java 7.) Как конечный пользователь, я хочу иметь последнюю версию Java по соображениям безопасности. , но как разработчик я не хочу забивать себе голову пользователями. Это чужая работа. Тем не менее, +1 во многом напоминает мне Python with, который мне очень нравится.
Я не согласен с тем, что это реальный ответ. Если у экземпляра есть ресурс, который он обрабатывает в течение более длительного периода времени при нескольких вызовах методов, тогда try-with-resources не поможет. Если только нельзя закрыть и снова открыть указанный ресурс с той скоростью, с которой вызываются указанные методы - это не общий факт.
В самом деле, это нет фактический ответ. Невозможно использовать эту структуру для управления разрушением объекта, если конструкция и использование объекта полностью не инкапсулированы try, а finally не используется для принудительного вызова obj.finalize(). И даже такая установка не решает проблемы, поставленной OP: разрушение объекта в середине программы, запускаемое кнопкой «сброс».
Другие пользователи показали, что это делается в точке входа в ваше приложение. Определите вашу переменную глобально. Инициализируйте его во входной функции с помощью try. Деинициализируйте файл finally (когда ваше приложение закрывается). Вполне возможно.
@nurettin Java 7 вышла всего 3 месяца назад, когда был задан вопрос, помогает ли это лучше понять.
@corsiKa, спасибо. Кроме того, за этот ответ больше не осталось мало голосов.
С выпуском 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.
что, если бы мы не использовали блок try-with-resources? Я думаю, что мы можем вызвать close в finalize () просто для того, чтобы убедиться, что close был вызван.
@shintoZ, как я читал в ответах выше, нет гарантии выполнения finalize()
Раньше я в основном имел дело с C++, и это тоже привело меня к поиску деструктора. Сейчас я много использую JAVA. То, что я сделал, и это может быть не лучший случай для всех, но я реализовал свой собственный деструктор, сбросив все значения на 0 или по умолчанию через функцию.
Пример:
public myDestructor() {
variableA = 0; //INT
variableB = 0.0; //DOUBLE & FLOAT
variableC = "NO NAME ENTERED"; //TEXT & STRING
variableD = false; //BOOL
}
В идеале это не будет работать для всех ситуаций, но там, где есть глобальные переменные, это будет работать, пока у вас их не будет тонны.
Я знаю, что я не лучший программист на Java, но, похоже, мне это помогает.
Старайтесь чаще использовать неизменяемые объекты, после того, как вы их «поймете», все станет более осмысленным :)
Это не столько неправильно, сколько бессмысленно - т.е. ничего не достигает. Если ваша программа требует, чтобы необработанные типы были сброшены для правильной работы, тогда ваши экземпляры классов имеют неправильную область видимости, что означает, что вы, вероятно, переназначаете свойства существующего объекта на свойства нового объекта, не создавая новый Object ().
За исключением того, что требуется множество сбросов переменных. Я выбрал деструктор имени, потому что он подходит для моей работы. Он чего-то добивается, вы ничего не понимаете
Я согласен с большинством ответов.
Вы не должны полностью полагаться ни на finalize, ни на ShutdownHook.
JVM не гарантирует, когда будет вызван этот метод finalize().
finalize() вызывается только один раз потоком GC. Если объект восстанавливается после завершения метода, то finalize не будет вызываться снова.
В вашем приложении могут быть некоторые живые объекты, для которых никогда не вызывается сборка мусора.
Любой Exception, выданный методом финализации, игнорируется потоком GC.
Методы System.runFinalization(true) и Runtime.getRuntime().runFinalization(true) увеличивают вероятность вызова метода finalize(), но теперь эти два метода устарели. Эти методы очень опасны из-за отсутствия безопасности потоков и возможного создания взаимоблокировок.
public void addShutdownHook(Thread hook)
Registers a new virtual-machine shutdown hook.
Виртуальная машина Java выключается в ответ на два типа событий:
Программа завершается нормально, когда завершается последний поток, не являющийся демоном, или когда вызывается метод выхода (эквивалентно System.exit), или
Виртуальная машина завершает работу в ответ на прерывание пользователя, такое как ввод ^ C, или общесистемное событие, такое как выход пользователя из системы или завершение работы системы.
Ловушка выключения - это просто инициализированный, но не запущенный поток. Когда виртуальная машина начинает свою последовательность выключения, она запускает все зарегистрированные перехватчики выключения в некотором неопределенном порядке и позволяет им работать одновременно. Когда все перехватчики завершены, он запустит все неактивированные финализаторы, если финализация при выходе была включена.
Наконец, виртуальная машина остановится. Обратите внимание, что потоки демона будут продолжать работать во время последовательности завершения работы, как и потоки, не являющиеся демонами, если завершение работы было инициировано вызовом метода выхода.
Крюки отключения также должны быстро завершить свою работу. Когда программа вызывает exit, ожидается, что виртуальная машина сразу же выключится и выйдет.
Но даже документация Oracle цитирует это
In rare circumstances the virtual machine may abort, that is, stop running without shutting down cleanly
Это происходит, когда виртуальная машина завершается извне, например, с помощью сигнала SIGKILL в Unix или вызова TerminateProcess в Microsoft Windows. Виртуальная машина также может завершить работу, если собственный метод не работает, например, из-за повреждения внутренних структур данных или попытки доступа к несуществующей памяти. Если виртуальная машина прерывается, то нельзя гарантировать, будут ли запущены какие-либо обработчики завершения работы.
Заключение: используйте блоки try{} catch{} finally{} соответствующим образом и освободите критические ресурсы в блоке finally(}. При освобождении ресурсов в блоке finally{} ловите Exception и Throwable.
Просто подумайте об исходном вопросе ... который, я думаю, мы можем сделать вывод из всех других изученных ответов, а также из существенного Эффективная Java Блоха, пункт 7, «Избегайте финализаторов», ищет решение законного вопроса способом, который является не соответствует языку Java ...:
... не было бы довольно очевидным решением сделать то, что на самом деле хочет OP, - это сохранить все ваши объекты, которые необходимо сбросить, в своего рода «манеж», на который все другие не сбрасываемые объекты имеют ссылки только через какой-то объекта доступа ...
А затем, когда вам нужно «перезагрузить», вы отключаете существующий манеж и создаете новый: вся сеть объектов в манеже отбрасывается по течению, чтобы никогда не возвращаться, и однажды будет собрана сборщиком мусора.
Если какой-либо из этих объектов является Closeable (или нет, но имеет метод close), вы можете поместить их в Bag в манеж по мере их создания (и, возможно, открытия), и последнее действие аксессуара перед отключением манежа будет быть перебирать все закрывающие их Closeables ...?
Код, вероятно, будет выглядеть примерно так:
accessor.getPlaypen().closeCloseables();
accessor.setPlaypen( new Playpen() );
closeCloseables, вероятно, будет методом блокировки, вероятно, с использованием защелки (например, CountdownLatch), для обработки (и ожидания, если это необходимо) любого Runnables / Callables в любых потоках, специфичных для Playpen, которые должны быть завершены соответствующим образом, в частности, в потоке JavaFX .
В Lombok есть аннотация @Cleanup, которая больше всего напоминает деструкторы C++:
@Cleanup
ResourceClass resource = new ResourceClass();
При его обработке (во время компиляции) Lombok вставляет соответствующий блок try-finally, так что resource.close() вызывается, когда выполнение выходит из области видимости переменной. Вы также можете явно указать другой метод для освобождения ресурса, например resource.dispose():
@Cleanup("dispose")
ResourceClass resource = new ResourceClass();
Я вижу преимущество в том, что будет меньше вложенности (что может быть значительным, если у вас много объектов, требующих «разрушения»).
Блок try-with-resource может иметь несколько ресурсов за один снимок
Но между ними не может быть инструкций.
Справедливый. Я полагаю, тогда рекомендуется предпочесть try-with-resource, даже для нескольких ресурсов, если между ними не требуется инструкций, которые вынуждают вас создавать новые блоки try-with-resource (увеличивая вложенность), а затем использовать @Cleanup
В Java нет точно класса деструктора, класс уничтожается в Java автоматически сборщиком мусора. но вы можете сделать это, используя один ниже, но это не совсем то же самое:
финализировать ()
Был вопрос, который вызвал всестороннее обсуждение финализации, так что при необходимости можно получить больше глубины ...
Здесь много отличных ответов, но есть дополнительная информация о том, почему вам следует избегать использования финализировать ().
Если JVM завершает работу из-за System.exit() или Runtime.getRuntime().exit(), финализаторы не будут запускаться по умолчанию. От Документация Javadoc для Runtime.exit ():
The virtual machine's shutdown sequence consists of two phases. In the first phase all registered shutdown hooks, if any, are started in some unspecified order and allowed to run concurrently until they finish. In the second phase all uninvoked finalizers are run if finalization-on-exit has been enabled. Once this is done the virtual machine halts.
Вы можете вызвать System.runFinalization(), но он делает «все возможное, чтобы завершить все незавершенные финализации» - не гарантия.
Есть метод System.runFinalizersOnExit(), но не используйте его - он небезопасен, давно устарел.
В Java нет деструкторов. Основная причина этого в Java - это сборщики мусора, которые всегда пассивно работают в фоновом режиме, и все объекты создаются в памяти кучи, то есть в том месте, где работает сборщик мусора. должны явно вызывать функцию удаления, поскольку нет такой вещи, как сборщик мусора.
В Java сборщик мусора автоматически удаляет неиспользуемые объекты, чтобы освободить память. Так что разумно, что в Java нет доступных деструкторов.
Утечка памяти возникает только в том случае, если вы храните ссылки на ненужные вам объекты. т.е. в вашей программе есть ошибка. Сборщик мусора будет работать по мере необходимости (иногда раньше)