Есть ли деструктор для Java?

есть ли деструктор для Java? Кажется, я не могу найти никакой документации по этому поводу. Если нет, как я могу достичь того же эффекта?

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

обычно на C/C++ программист, я думал, что это будет легко реализовать. (И поэтому я планировал осуществить его последним.) Я структурировал свою программу таким образом, чтобы все объекты с возможностью сброса были в одном классе, чтобы я мог просто уничтожить все "живые" объекты при нажатии кнопки сброса.

Я думал, что если все, что я сделал, это просто разыменовать данные и ждать сборщика мусора, чтобы собрать их, не будет ли утечки памяти, если мой пользователь неоднократно вводил данные и нажимал кнопку reset? Я также думал, поскольку Java довольно зрелый язык, должен быть способ предотвратить это или изящно решить эту проблему.

20 ответов


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

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

не было вопрос, который породил углубленное обсуждение метода finalize недавно, так что при необходимости следует обеспечить большую глубину...


нет, здесь нет деструкторов. Причина в том, что все объекты Java-это выделенная куча и собранный мусор. Без явного освобождения (т. е. оператора удаления C++) нет разумного способа реализации реальных деструкторов.

Java поддерживает финализаторы, но они предназначены для использования только в качестве защиты объектов, содержащих дескриптор собственных ресурсов, таких как сокеты, дескрипторы файлов, дескрипторы окон и т. д. Когда сборщик мусора собирает объект без финализатора, он просто помечает область памяти как свободную, и все. Когда объект имеет финализатор, он сначала копируется во временное местоположение (помните, что мы собираем мусор здесь), затем он помещается в очередь ожидания завершения, а затем поток финализатора опрашивает очередь с очень низким приоритетом и запускает финализатор.

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


если вы используете Java 7, посмотрите на try-with-resources заявление. Например:

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
  System.out.println(br.readLine());
} catch (Exception e) {
  ...
} finally {
  ...
}

здесь ресурс, который больше не нужен, освобождается в BufferedReader.close() метод. Вы можете создать свой собственный класс, реализующий AutoCloseable и используйте его аналогичным образом.

это утверждение более ограничено, чем finalize С точки зрения структурирования кода, но в то же время это делает код проще понять и поддержать. Кроме того, нет никакой гарантии, что а finalize метод вызывается во время livetime приложения.


использование finalize () методы следует избегать. Они не являются надежным механизмом очистки ресурсов, и можно вызвать проблемы в сборщике мусора, злоупотребляя ими.

Если вам требуется вызов освобождения объекта, скажем, для освобождения ресурсов, используйте явный вызов метода. Это соглашение можно увидеть в существующих API (например,выходит, графика.dispose (), виджет.dispose ()) и обычно вызывается через try / finally.

Resource r = new Resource();
try {
    //work
} finally {
    r.dispose();
}

попытки использовать удаленный объект должны вызывать исключение среды выполнения (см. IllegalStateException).


изменить:

Я думал, если я просто разыменовать данные и ждать сборщик мусора, чтобы собрать их, не будет ли утечки памяти, если мой пользователь повторно ввел данные и нажал кнопку сброса?

В общем, все, что вам нужно для этого нужно разыменовать объекты - по крайней мере, так это должно работать. Если вы беспокоитесь о сборе мусора, проверьте Java SE 6 HotSpot[tm] настройка сбора мусора виртуальной машины (или эквивалентный документ для вашей версии 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 предложения.


Я полностью согласен с другими ответами, говоря, чтобы не полагаться на выполнение finalize.

в дополнение к try-catch-finally блоков, вы можете использовать среда выполнения#addShutdownHook (введено в Java 1.6) для выполнения окончательной очистки в вашей программе.

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

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


нет, java.lang.Object#finalize это самое близкое, что вы можете получить.

однако, когда (и если) он вызывается, не гарантируется.
См.: java.lang.Runtime#runFinalizersOnExit(boolean)


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

вы можете быть уведомлены после того, как объект был уничтожен с помощью java.ленг.ссылка.PhantomReference (на самом деле, говоря, что он имеет быть уничтоженным может быть немного неточным, но если фантомная ссылка на него помещена в очередь, то она больше не восстанавливается, что обычно составляет то же самое). Общее использование:

  • выделите ресурс (ы) в своем классе, который должен быть разрушен в другой вспомогательный объект (обратите внимание, что если все, что вы делаете, это закрытие соединения, что является обычным случаем, вам не нужно писать новый класс: соединение, которое будет закрыто, будет "вспомогательным объектом" в этом случай.)
  • когда вы создаете свой основной объект, создайте также PhantomReference к нему. Либо обратитесь к новому вспомогательному объекту, либо настройте карту из объектов PhantomReference к соответствующим вспомогательным объектам.
  • после того, как основной объект собран, PhantomReference помещается в очередь (или, скорее, он может быть помещен в очередь, как финализаторы, нет никакой гарантии, что он когда - либо будет, например, если VM выходит, то он не будет ждать). Убедитесь, что вы обрабатываете его очередь (либо в специальная нить или время от времени). Из-за жесткой ссылки на вспомогательный объект вспомогательный объект еще не был собран. Поэтому сделайте любую очистку на вспомогательном объекте, затем отбросьте PhantomReference, и помощник в конечном итоге тоже будет собран.

существует также finalize (), который выглядит как деструктор, но не ведет себя как таковой. Обычно это не лучший вариант.


извините, если это отклоняется от основной темы, но java.утиль.Документация Timer (SE6) гласит:

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

Я хотел бы вызвать cancel на класс, владеющий таймером, теряющим свою последнюю ссылку (или immeditalesky раньше). Здесь надежный деструктор мог бы сделать это за меня. Комментарии выше указывают на то, что, наконец, это плохой выбор, но есть ли элегантное решение? Это дело "...способный держать применение от прекращать..." есть не привлекательно.


Если вы беспокоитесь только о памяти, не надо. Просто доверяйте GC, он делает достойную работу. Я действительно видел, что это настолько эффективно, что для производительности было бы лучше создавать кучи крошечных объектов, чем использовать большие массивы в некоторых случаях.


на finalize() функция деструктор.

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

кроме того, требуется более одного GC для освобождения объектов, которые имеют finalize().

вы должны попытаться очистить логические места в коде с помощью try{...} finally{...} заявления!


Я согласен с большинством ответов.

вы не должны полностью зависеть от любого finalize или ShutdownHook

завершить

  1. JVM не гарантирует, когда это finalize() метод будет вызван.

  2. finalize() вызывается только один раз потоком GC, если объект возрождается из метода завершения, чем finalize не будет вызываться снова.

  3. в приложения, у вас могут быть некоторые живые объекты, на которых сборка мусора никогда не вызывается.

  4. любое исключение, вызванное методом завершения, игнорируется потоком GC

  5. System.runFinalization(true) и Runtime.getRuntime().runFinalization(true) методы увеличивают вероятность применения finalize() метод, но теперь эти два метода устарели. Эти методы очень опасны из-за отсутствия безопасности потоков и возможного создания тупика.

shutdownHooks

public void addShutdownHook(Thread hook)

регистрирует новый крюк выключения виртуальной машины.

виртуальная машина Java завершает работу в ответ на два вида событий:

  1. программа завершает работу нормально, когда завершается последний не-демонический поток или когда завершается (эквивалентно, система.exit) вызывается метод, или
  2. виртуальная машина завершается в ответ на пользователя прерывание, например ввод ^C, или общесистемное событие, например выход пользователя из системы или завершение работы системы.
  3. крюк выключения - это просто инициализированный, но не выделенный поток. Когда виртуальная машина начнет свою последовательность выключения, она запустит все зарегистрированные крючки выключения в некотором неопределенном порядке и позволит им работать одновременно. Когда все крючки закончены, он будет запускать все незваные финализаторы, если включено finalization-on-exit.
  4. наконец, виртуальная машина остановится. Обратите внимание, что потоки демона будут продолжать работать во время последовательности завершения работы, как и потоки не-демона, если завершение работы было инициировано путем вызова метода exit.
  5. выключение крючки также должны закончить свою работу быстро. Когда программа вызывает exit, ожидается, что виртуальная машина быстро завершит работу и выйдет.

    но даже документация Oracle цитировала это

в редких случаях виртуальная машина может прерваться, то есть перестать работать без завершения работы чисто

это происходит, когда виртуальная машина завершается извне, например с SIGKILL сигнал на Unix или TerminateProcess вызов Microsoft Windows. Виртуальная машина также может прерваться, если собственный метод идет наперекосяк, например, повреждая внутренние структуры данных или пытаясь получить доступ к несуществующей памяти. Если виртуальная машина прерывается, то нет никакой гарантии, что не будут запущены никакие крючки выключения.

вывод : использовать try{} catch{} finally{} блоки соответствующим образом и освободить критические ресурсы в finally(} блок. Во время выпуска ресурсов в finally{} блок, поймать Exception и Throwable.


возможно, вы можете попробовать ... наконец, блок для завершения объекта в потоке управления, в котором вы используете объект. Конечно, это не происходит автоматически, но и разрушение в C++не происходит. Вы часто видите закрытие ресурсов в блоке finally.


ближайшим эквивалентом деструктора в Java является finalize () метод. Большая разница для традиционного деструктора заключается в том, что вы не можете быть уверены, когда он будет вызван, так как это ответственность сборщика мусора. Я настоятельно рекомендую внимательно прочитать это перед использованием, так как ваши типичные шаблоны RAIA для дескрипторов файлов и так далее не будут надежно работать с finalize().


есть @Cleanup аннотация на Ломбоке, которая в основном напоминает деструкторы C++:

@Cleanup
ResourceClass resource = new ResourceClass();

при его обработке (во время компиляции) Ломбок вставляет соответствующий try-finally блок, так что resource.close() вызывается, когда выполнение покидает область действия переменной. Вы также можете явно указать другой метод освобождения ресурса, например resource.dispose():

@Cleanup("dispose")
ResourceClass resource = new ResourceClass();

хотя были значительные достижения в технологии 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().

явно не то, что вы хочу, но может быть, что другие люди ищут.


просто думаю о первоначальном вопросе... что, я думаю, мы можем заключить из всех других полученных ответов, а также из существенного Эффективная Java, пункт 7, "Избегайте финализаторов", ищет решение законного вопроса таким образом, который не соответствует языку Java...:

... не было бы довольно очевидным решением сделать то, что на самом деле хочет OP, чтобы сохранить все ваши объекты, которые нужно сбросить в своего рода "манеж", к которому все другие объекты, не сбрасываемые, имеют ссылки только через какой-либо объект доступа...

и затем, когда вам нужно "сбросить", вы отключаете существующий манеж и делаете новый: вся паутина объектов в манеже отбрасывается, никогда не возвращается и однажды будет собрана GC.

если любой из этих объектов Closeable (или нет, но есть close способ) вы можете поместить их в Bag в Манеже по мере их создания (и, возможно, открытия), и последний акт аксессора перед тем, как отрезать манеж, должен был пройти через все Closeables закрывая их... ?

код, вероятно, будет выглядеть примерно так:

accessor.getPlaypen().closeCloseables();
accessor.setPlaypen( new Playpen() );

closeCloseables вероятно, будет методом блокировки, возможно, с использованием защелки (например,CountdownLatch), чтобы иметь дело с (и ждать, когда это уместно) любой Runnables/Callables в любых потоках, специфичных для Playpen должно быть закончено соответствующим образом, в частности в потоке JavaFX.


в Java нет точно класса деструктора, класс уничтожается в java автоматически сборщиком мусора . но вы можете сделать это, используя ниже один, но это не то же :

finalize ()

было вопрос, который породил углубленное обсуждение метода finalize, так что вы должны получить больше глубины, если это необходимо...


Я имел дело в основном с C++, и это привело меня к поиску деструктора. Я сейчас много использую JAVA. То, что я сделал, и это может быть не лучший случай для всех, но я реализовал свой собственный деструктор, сбросив все значения до 0 или там по умолчанию через функцию.

пример:

public myDestructor() {

variableA = 0; //INT
variableB = 0.0; //DOUBLE & FLOAT
variableC = "NO NAME ENTERED"; //TEXT & STRING
variableD = false; //BOOL

}

В идеале это не будет работать для всех ситуаций, но там, где есть глобальные переменные, он будет работать, пока у вас нет тонны их.

Я знаю, что я не лучший программист Java, но, похоже, он работает для меня.