Зачем вам реализовывать finalize ()? - PullRequest
355 голосов
/ 01 октября 2008

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

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

Итак, мой вопрос: какие существуют варианты использования для реализации finalize(), которые не могут быть обработаны более надежно с помощью другого процесса или синтаксиса в языке?

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

Ответы [ 21 ]

5 голосов
/ 01 октября 2008

Чтобы выделить точку в ответах выше: финализаторы будут выполняться в одиночном потоке GC. Я слышал о крупной демонстрации Sun, где разработчики добавили небольшой сон некоторым финализаторам и намеренно поставили на колени необычную 3D-демонстрацию.

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

У Eckel's Thinking in Java есть хороший раздел по этому вопросу.

3 голосов
/ 01 октября 2008

Будьте осторожны с тем, что вы делаете в finalize(). Особенно, если вы используете его для таких вещей, как вызов close () для обеспечения очистки ресурсов. Мы столкнулись с несколькими ситуациями, когда у нас были библиотеки JNI, связанные с работающим java-кодом, и при любых обстоятельствах, когда мы использовали finalize () для вызова методов JNI, мы могли получить очень плохое повреждение кучи Java. Повреждение не было вызвано самим исходным кодом JNI, все трассировки памяти были в порядке в собственных библиотеках. Это был тот факт, что мы вообще вызывали методы JNI из finalize ().

Это было с JDK 1.5, который все еще широко используется.

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

3 голосов
/ 01 октября 2008

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

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

3 голосов
/ 01 октября 2008

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

Многие драйверы баз данных делают это в своих реализациях Statement и Connection, чтобы обеспечить небольшую безопасность от разработчиков, которые забывают вызывать close для них.

2 голосов
/ 07 апреля 2011

Может быть удобно удалить вещи, которые были добавлены в глобальное / статическое место (по необходимости) и должны быть удалены при удалении объекта. Например:

    private void addGlobalClickListener() {
        weakAwtEventListener = new WeakAWTEventListener(this);

        Toolkit.getDefaultToolkit().addAWTEventListener(weakAwtEventListener, AWTEvent.MOUSE_EVENT_MASK);
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();

        if(weakAwtEventListener != null) {
            Toolkit.getDefaultToolkit().removeAWTEventListener(weakAwtEventListener);
        }
    }
2 голосов
/ 03 февраля 2010

Редактировать: Хорошо, это действительно не работает. Я реализовал это и подумал, что если это иногда не удается, это нормально для меня, но он даже не вызывал метод finalize один раз.

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

@Override
public void finalize()
{
    try {saveCache();} catch (Exception e)  {e.printStackTrace();}
}

public void saveCache() throws FileNotFoundException, IOException
{
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("temp/cache.tmp"));
    out.writeObject(cache);
}
0 голосов
/ 04 июня 2018

В качестве примечания:

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

Источник: finalize () устарел на java-9

0 голосов
/ 24 июня 2015

Ресурсы (Файл, Сокет, Поток и т. Д.) Должны быть закрыты, как только мы закончим с ними. У них обычно есть метод close(), который мы обычно вызываем в разделе finally операторов try-catch. Иногда finalize() также может использоваться несколькими разработчиками, но IMO не подходит, так как нет гарантии, что финализация будет вызываться всегда.

В Java 7 у нас есть оператор try-with-resources , который можно использовать как:

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
  // Processing and other logic here.
} catch (Exception e) {
  // log exception
} finally {
  // Just in case we need to do some stuff here.
}

В приведенном выше примере try-with-resource автоматически закроет ресурс BufferedReader, вызвав метод close(). Если мы хотим, мы также можем реализовать Closeable в наших собственных классах и использовать его аналогичным образом. ИМО кажется более аккуратным и простым для понимания.

0 голосов
/ 16 марта 2015

Лично я почти никогда не использовал finalize(), за исключением одного редкого случая: я создал собственную коллекцию универсального типа и написал собственный метод finalize(), который выполняет следующие действия:

public void finalize() throws Throwable {
    super.finalize();
    if (destructiveFinalize) {
        T item;
        for (int i = 0, l = length(); i < l; i++) {
            item = get(i);
            if (item == null) {
                continue;
            }
            if (item instanceof Window) {
                ((Window) get(i)).dispose();
            }
            if (item instanceof CompleteObject) {
                ((CompleteObject) get(i)).finalize();
            }
            set(i, null);
        }
    }
}

(CompleteObject - это созданный мной интерфейс, позволяющий вам указать, что вы реализовали редко реализуемые Object методы, такие как #finalize(), #hashCode() и #clone())

Таким образом, используя сестринский метод #setDestructivelyFinalizes(boolean), программа, использующая мою коллекцию, может (помочь) гарантировать, что уничтожение ссылки на эту коллекцию также уничтожает ссылки на ее содержимое и удаляет все окна, которые могут непреднамеренно поддерживать JVM. Я также подумал о том, чтобы остановить любые темы, но это открыло новую банку с червями.

0 голосов
/ 03 декабря 2014

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

Однако этот ответ показывает, что по крайней мере в java8 с JIT-компилятором вы сталкиваетесь с неожиданными проблемами, когда иногда вызывается финализатор даже до того, как вы закончите чтение из потока, поддерживаемого вашим объектом.

Таким образом, даже в этой ситуации вызов finalize будет не рекомендуемым.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...