Мне нужно знать, когда вызывается метод finalize()
в JVM
. Я создал тестовый класс, который записывает в файл при вызове метода finalize()
, переопределяя его. Это не выполнено. Кто-нибудь может сказать мне причину, почему это не выполняется?
Когда метод finalize() вызывается в Java?
Ответ 1
В общем, лучше не полагаться на finalize()
для очистки и т.д.
В соответствии с Javadoc (что было бы полезно прочитать), это:
Вызывается сборщиком мусора на объекте, когда сбор мусора определяет, что больше нет ссылок на объект.
Как отметил Йоахим, это может никогда не произойти в жизни программы, если объект всегда доступен.
Кроме того, сборщик мусора не может запускаться в любое конкретное время. В общем, то, что я пытаюсь сказать, это finalize()
, вероятно, не лучший метод для использования вообще, если для этого не требуется что-то конкретное.
Ответ 2
Метод finalize
вызывается, когда объект собирается собирать мусор. Это может быть в любое время после того, как оно получило право на сбор мусора.
Обратите внимание, что вполне возможно, что объект никогда не собирает мусор (и поэтому finalize
никогда не вызывается). Это может произойти, когда объект никогда не становится подходящим для gc (поскольку он доступен на протяжении всего срока службы JVM) или когда сбор мусора фактически не выполняется между временем, когда объект становится подходящим, и временем, когда JVM перестает работать (это часто происходит с простым тестовые программы).
Есть способы рассказать JVM о запуске finalize
для объектов, на которые он еще не был вызван, но использовать их также не очень хорошо (гарантии этого метода также не очень сильны).
Если вы полагаетесь на finalize
для правильной работы вашего приложения, вы делаете что-то неправильно. finalize
должен использоваться только для очистки (обычно не Java) ресурсов. И это именно потому, что JVM не гарантирует, что finalize
когда-либо вызывается на любом объекте.
Ответ 3
protected void finalize() throws Throwable {}
- каждый класс наследует метод
finalize()
из java.lang.Object- метод вызывается сборщиком мусора, когда он определяет больше ссылок на объект существует
- Метод finalize объекта не выполняет никаких действий, но может быть переопределен любой класс
- обычно он должен быть переопределен для очистки ресурсов, отличных от Java, т.е. закрытие файл
если переопределить
finalize()
, хорошая практика программирования - использовать утверждение try-catch-finally и всегда вызывайтеsuper.finalize()
. Эта является мерой безопасности для обеспечения того, чтобы вы не случайно пропустить закрытие ресурс, используемый вызовами объектов классprotected void finalize() throws Throwable { try { close(); // close open files } finally { super.finalize(); } }
любое исключение, созданное
finalize()
во время сбора мусора, останавливает завершение, но в противном случае игнорируетсяfinalize()
никогда не запускается более одного раза на любом объекте
цитата из: http://www.janeg.ca/scjp/gc/finalize.html
Вы также можете проверить эту статью:
Ответ 4
Метод Java finalize()
не является деструктором и не должен использоваться для обработки логики, от которой зависит ваше приложение. Спецификация Java заявляет, что нет гарантии, что метод finalize
вообще вызван во время жизни приложения.
То, что вы, вероятно, хотите, это комбинация finally
и метода очистки, как в:
MyClass myObj;
try {
myObj = new MyClass();
// ...
} finally {
if (null != myObj) {
myObj.cleanup();
}
}
Ответ 5
Отъезд Эффективная Java, 2-е издание стр. 27. Пункт 7: Избегайте финализаторов
Финализаторы непредсказуемы, часто опасны и вообще не нужны. никогда не делают ничего критически важного в финализаторе. никогда зависят от финализатора для обновления критического состояния.
Чтобы завершить работу ресурса, вместо этого используйте try-finally:
// try-finally block guarantees execution of termination method Foo foo = new Foo(...); try { // Do what must be done with foo ... } finally { foo.terminate(); // Explicit termination method }
Ответ 6
Когда метод
finalize()
вызывается в Java?
Метод finalize будет вызываться после того, как GC обнаружит, что объект больше недоступен, и до того, как он действительно восстановит память, используемую объектом.
-
Если объект никогда не становится недоступным,
finalize()
никогда не будет вызываться на нем. -
Если GC не запускается, то
finalize()
никогда не может быть вызван. (Обычно GC работает только тогда, когда JVM решает, что, вероятно, будет достаточно мусора, чтобы сделать его полезным.) -
Может потребоваться более одного цикла GC до того, как GC определит, что конкретный объект недоступен. (Java GCs обычно являются "генераторными" коллекторами...)
-
Как только GC обнаружит объект, он недоступен и финиширован, он находится в очереди финализации. Финализация обычно происходит асинхронно с обычным GC.
(Спецификация JVM фактически позволяет JVM никогда не запускать финализаторы... при условии, что она не возвращает пространство, используемое объектами. JVM, который был реализован таким образом, будет искалечен/бесполезен, но это это поведение "разрешено".)
Результат заключается в том, что нецелесообразно полагаться на финализацию, чтобы делать то, что нужно сделать в определенный временной интервал. Это "лучшая практика", чтобы не использовать их вообще. Должен быть лучший (то есть более надежный) способ делать то, что вы пытаетесь сделать в методе finalize()
.
Единственное законное использование для финализации - очистка ресурсов, связанных с объектами, которые были потеряны кодом приложения. Даже тогда вы должны попытаться написать код приложения, чтобы он не потерял объекты в первую очередь. (Например, используйте Java 7+ try-with-resources, чтобы гарантировать, что close()
всегда вызывается...)
Я создал тестовый класс, который записывает в файл, когда метод finalize() вызывается путем его переопределения. Он не выполнен. Может ли кто-нибудь сказать мне причину, по которой он не выполняется?
Трудно сказать, но есть несколько возможностей:
- Объект не является сборкой мусора, поскольку он все еще доступен.
- Объект не собирает мусор, потому что GC не запускается до завершения теста.
- Объект найден GC и помещен в очередь завершения GC, но завершение не завершено до завершения теста.
Ответ 7
Поскольку существует неопределенность в вызове метода finalize() JVM (не уверен, будет ли выполняться finalize(), который переопределен, или нет), для целей обучения лучший способ наблюдать, что происходит, когда вызывается finalize(), вынуждает JVM вызывать сбор мусора командой System.gc()
.
В частности, finalize() вызывается, когда объект больше не используется. Но когда мы пытаемся назвать это, создавая новые объекты, нет уверенности в его вызове. Поэтому для уверенности мы создаем объект null
c
, который, очевидно, не имеет будущего использования, поэтому мы видим, что объект c
завершает вызов.
Пример
class Car {
int maxspeed;
Car() {
maxspeed = 70;
}
protected void finalize() {
// Originally finalize method does nothing, but here we override finalize() saying it to print some stmt
// Calling of finalize is uncertain. Difficult to observe so we force JVM to call it by System.gc(); GarbageCollection
System.out.println("Called finalize method in class Car...");
}
}
class Bike {
int maxspeed;
Bike() {
maxspeed = 50;
}
protected void finalize() {
System.out.println("Called finalize method in class Bike...");
}
}
class Example {
public static void main(String args[]) {
Car c = new Car();
c = null; // if c weren`t null JVM wouldn't be certain it cleared or not, null means has no future use or no longer in use hence clears it
Bike b = new Bike();
System.gc(); // should clear c, but not b
for (b.maxspeed = 1; b.maxspeed <= 70; b.maxspeed++) {
System.out.print("\t" + b.maxspeed);
if (b.maxspeed > 50) {
System.out.println("Over Speed. Pls slow down.");
}
}
}
}
Выход
Called finalize method in class Car...
1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32 33 34 35 36 37 38 39
40 41 42 43 44 45 46 47 48 49
50 51Over Speed. Pls slow down.
52Over Speed. Pls slow down.
53Over Speed. Pls slow down.
54Over Speed. Pls slow down.
55Over Speed. Pls slow down.
56Over Speed. Pls slow down.
57Over Speed. Pls slow down.
58Over Speed. Pls slow down.
59Over Speed. Pls slow down.
60Over Speed. Pls slow down.
61Over Speed. Pls slow down.
62Over Speed. Pls slow down.
63Over Speed. Pls slow down.
64Over Speed. Pls slow down.
65Over Speed. Pls slow down.
66Over Speed. Pls slow down.
67Over Speed. Pls slow down.
68Over Speed. Pls slow down.
69Over Speed. Pls slow down.
70Over Speed. Pls slow down.
Примечание. Даже после печати до 70 и после того, какой объект b не используется в программе, существует неопределенность в том, что b очищается или нет с помощью JVM, так как "Вызывается метод завершения в классе Bike..." не печатается.
Ответ 8
finalize будет распечатывать счетчик для создания класса.
protected void finalize() throws Throwable {
System.out.println("Run F" );
if ( checkedOut)
System.out.println("Error: Checked out");
System.out.println("Class Create Count: " + classCreate);
}
Основной
while ( true) {
Book novel=new Book(true);
//System.out.println(novel.checkedOut);
//Runtime.getRuntime().runFinalization();
novel.checkIn();
new Book(true);
//System.runFinalization();
System.gc();
Как вы можете видеть. Следующий вывод показывает, что gc был выполнен в первый раз, когда число классов равно 36.
C:\javaCode\firstClass>java TerminationCondition
Run F
Error: Checked out
Class Create Count: 36
Run F
Error: Checked out
Class Create Count: 48
Run F
Ответ 9
В последнее время борясь с методами финализатора (чтобы избавиться от пулов соединений во время тестирования), я должен сказать, что финализатору не хватает многих вещей. Используя VisualVM для наблюдения, а также используя слабые ссылки для отслеживания фактического взаимодействия, я обнаружил, что в среде Java 8 выполняются следующие вещи (Oracle JDK, Ubuntu 15):
- Finalize не вызывается сразу, когда Finalizer (GC part) индивидуально владеет эталоном неуловимо
- По умолчанию сборщики пулов Garbage собирают недостижимые объекты
- Finalize вызывается в массе, указывающем на деталь реализации, что есть определенная фаза, сборщик мусора освобождает ресурсы.
- Вызов System.gc() часто не приводит к тому, что объекты, которые будут финализироваться чаще, просто приводят к тому, что Finalizer быстрее узнает о недоступном объекте.
- Создание дампа потока почти всегда приводит к запуску финализатора из-за высокой накладной кучи во время выполнения дампа кучи или другого внутреннего механизма.
- Фиксирующие швы должны быть связаны как требованиями памяти (освободите больше памяти), так и списком объектов, помеченных для окончательной обработки, растущих с определенным внутренним пределом. Поэтому, если у вас есть много объектов, которые будут завершены, фаза завершения будет срабатывать чаще и раньше по сравнению с несколькими
- Были случаи, когда System.gc() вызвал финализацию напрямую, но только если ссылка была локальной и короткой. Это может быть связано с генерацией.
Заключительная мысль
Метод finalize является ненадежным, но может использоваться только для одной вещи. Вы можете убедиться, что объект был закрыт или удален до сбора мусора, что позволяет реализовать отказоустойчивость, если обрабатываются объекты с более сложным жизненным циклом, включающие действие с истекшим сроком действия. Именно по этой причине я могу думать, что это стоит того, чтобы переопределить его.
Ответ 10
finalize не гарантируется. Этот метод вызывается, когда объект становится подходящим для GC. Существует много ситуаций, когда объекты не могут быть собраны в мусор.
Ответ 11
Объект становится доступным для сбора мусора или GC, если его невозможно достичь из любых живых потоков или любых статических рефренсов, другими словами, вы можете сказать, что объект становится пригодным для сбора мусора, если все его ссылки равны нулю. Циклические зависимости не считаются ссылкой, поэтому, если объект А имеет ссылку на объект B, а объект B имеет ссылку на объект A, и у них нет другой живой ссылки, то оба объекта A и B будут иметь право на сбор мусора. Как правило, объект становится пригодным для сбора мусора в Java в следующих случаях:
- Все ссылки этого объекта явно заданы равными нулю, например. object = null
- Объект создается внутри блока, а ссылка выходит из области действия после того, как элемент управления выйдет из этого блока.
- Объект родительского объекта имеет значение null, если объект содержит ссылку на другой объект, и когда вы устанавливаете ссылку на объект контейнера null, дочерний или содержащий объект автоматически становится пригодным для сбора мусора.
- Если у объекта есть только живые ссылки через WeakHashMap, он будет иметь право на сбор мусора.
Ответ 12
Иногда, когда он уничтожается, объект должен совершить действие. Например, если объект имеет не-java-ресурс, такой как дескриптор файла или шрифт, вы можете проверить, освобождены ли эти ресурсы до уничтожения объекта. Для управления такими ситуациями java предлагает механизм, называемый "финализация". Завершая его, вы можете определить конкретные действия, которые происходят, когда объект собирается быть удален из сборщика мусора. Чтобы добавить финализатор в класс, просто определите метод finalize(). Время выполнения Java вызывает этот метод всякий раз, когда он собирается удалить объект этого класса. В рамках метода finalize() вы указываете действия, которые необходимо выполнить перед уничтожением объекта. Сборщик мусора периодически ищет объекты, которые больше не ссылаются ни на какое рабочее состояние, ни косвенно на любой другой объект со ссылкой. Перед выпуском актива среда выполнения Java вызывает метод finalize() для объекта. Метод finalize() имеет следующий общий вид:
protected void finalize(){
// This is where the finalization code is entered
}
С ключевым словом protected предотвращается доступ к finalize() кодом вне его класса. Важно понимать, что finalize() вызывается непосредственно перед сборкой мусора. Он не вызывается, когда объект выходит из области, например. Это означает, что вы не можете знать, когда, или если, finalize() будет выполнен. В результате программа должна предоставлять другие средства для освобождения системных ресурсов или других ресурсов, используемых объектом. Вы не должны полагаться на finalize() для нормальной работы программы.
Ответ 13
Класс, где мы переопределяем метод finalize
public class TestClass {
public TestClass() {
System.out.println("constructor");
}
public void display() {
System.out.println("display");
}
@Override
public void finalize() {
System.out.println("destructor");
}
}
Вероятность вызова метода finalize
public class TestGarbageCollection {
public static void main(String[] args) {
while (true) {
TestClass s = new TestClass();
s.display();
System.gc();
}
}
}
когда память перегружена объектами дампа, gc вызовет метод finalize
запустите и просмотрите консоль, где вы не часто вызываете метод finalize, когда память перегружается, тогда вызывается метод finalize.
Ответ 14
Java позволяет объектам реализовывать метод finalize() которые могут быть вызваны.
Методfinalize() вызывается, если сборщик мусора пытается собирать объект.
Если сборщик мусора не запускается, метод не вызывается.
Если сборщик мусора не может собрать объект и пытается запустить снова, метод не вызывается во второй раз.
На практике вы вряд ли сможете использовать его в реальных проектах.
Просто имейте в виду, что он не может быть вызван и что он определенно не будет вызываться дважды. Метод finalize() может начинаться с нуля или один время.
В следующем коде метод finalize() не выводит результат, когда мы запустите его, так как программа выйдет, прежде чем возникнет необходимость запустить сборщик мусора.
Ответ 15
finalize()
вызывается непосредственно перед сборкой мусора. Он не вызывается, когда объект выходит из области видимости. Это означает, что вы не можете знать, когда или даже выполнится finalize()
.
Пример:
Если ваша программа завершится до того, как произойдет сборщик мусора, то finalize()
не будет выполнен. Следовательно, его следует использовать в качестве процедуры резервного копирования для обеспечения надлежащей обработки других ресурсов или для приложений специального назначения, а не в качестве средства, которое ваша программа использует в своей нормальной работе.
Ответ 16
Попробуйте выполнить эту программу для лучшего понимания
public class FinalizeTest
{
static {
System.out.println(Runtime.getRuntime().freeMemory());
}
public void run() {
System.out.println("run");
System.out.println(Runtime.getRuntime().freeMemory());
}
protected void finalize() throws Throwable {
System.out.println("finalize");
while(true)
break;
}
public static void main(String[] args) {
for (int i = 0 ; i < 500000 ; i++ ) {
new FinalizeTest().run();
}
}
}