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

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

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

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

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

Ответ 1

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

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

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

Ответ 2

Взгляните на инструкцию " попробуй с ресурсами". Например:

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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

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


EDIT:

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

Как правило, все, что вам нужно сделать, это разыменовать объекты - по крайней мере, так оно и должно работать. Если вы беспокоитесь о сборке мусора, ознакомьтесь с Java SE 6 HotSpot [tm] Настройка виртуальной машины Garbage Collection Tuning (или эквивалентный документ для вашей версии JVM).

Ответ 5

С выпуском 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.

Ответ 6

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

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

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

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

Ответ 8

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

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

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

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

Ответ 9

Прошу прощения, если это отклоняется от основной темы, но в документации java.util.Timer(SE6) говорится:

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

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

Ответ 10

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

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

завершать

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

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

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

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

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

shutdownHooks

public void addShutdownHook(Thread hook)

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

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

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

    Но даже в документации Oracle указано, что

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

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

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

Ответ 11

Функция finalize() является деструктором.

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

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

Вы должны попытаться очистить логические места в вашем коде, используя операторы try{...} finally{...} !

Ответ 12

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

Ответ 13

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

Ответ 14

В Lombok есть примечание @Cleanup, которое в основном напоминает деструкторы С++:

@Cleanup
ResourceClass resource = new ResourceClass();

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

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

Ответ 15

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

Ответ 16

Если вы пишете 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().

Очевидно, что вы не хотите, но, возможно, это то, что ищут другие люди.

Ответ 17

Несмотря на значительные улучшения в технологии Java GC, вам все равно нужно помнить о ваших рекомендациях. Приходят на ум многочисленные случаи, казалось бы, тривиальных эталонных образцов, которые на самом деле являются крысами, гнездами под капотом.

Из вашего сообщения это не похоже на то, что вы пытаетесь реализовать метод reset для повторного использования объектов (true?). Являются ли ваши объекты имеющими какой-либо другой тип ресурсов, которые необходимо очистить (т.е. Потоки, которые должны быть закрыты, любые объединенные или заимствованные объекты, которые должны быть возвращены)? Если единственное, о чем вы беспокоитесь, это память dealloc, тогда я бы пересмотрел свою структуру объектов и попытался проверить, что мои объекты являются самосогласованными структурами, которые будут очищены в GC time.

Ответ 18

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

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

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

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

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

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

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

Ответ 19

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

завершить()

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

Ответ 20

Здесь много хороших ответов, но есть дополнительная информация о том, почему вы должны избегать использования finalize().

Если JVM завершает работу из-за System.exit() или Runtime.getRuntime().exit(), финализаторы не будут запускаться по умолчанию. Из Javadoc для Runtime.exit()

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

Вы можете вызвать System.runFinalization() но он только "делает все возможное, чтобы завершить все незавершенные финализации", а не является гарантией.

Есть метод System.runFinalizersOnExit(), но не используйте его - он небезопасен, давно System.runFinalizersOnExit() устаревшим.

Ответ 21

Я думаю, что люди не понимают, что, поскольку у Java нет деструкторного метода, программисту нужно по-разному думать о владении ресурсом. Например:

Следующий класс поддерживает право собственности на ресурс (в данном случае соединение с базой данных).

public class Database {
  Connection conn = null;
  public void getConnection(String databasename) {
     conn = new MakeConnection();
  }
  public void close() {
     conn.close();
  }
}

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

Вот почему лучше просто отключить ресурс.

public class Database {
  public Connection getConnection(String databasename) {
      Connection conn = null;
      conn = new MakeConnection()
      return conn;
  }
}

Этот метод передает стандартный инициализированный класс соединения java api. У этого есть близкий метод, который все знают, чтобы позвонить.

Дело в том, чтобы работать с языком, а не пытаться сделать его на другом языке.

Ответ 22

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

Пример:

public myDestructor() {

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

}

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

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