Закрывается ли Input/OutputStreams при уничтожении?

Закрывает ли InputStreams и OutputStreams в Java() при уничтожении? Я полностью понимаю, что это может быть дурным тоном (особенно в мире C и C++), но мне любопытно.

Кроме того, предположим, что у меня есть следующий код:

private void foo()
{
    final string file = "bar.txt";
    Properties p = new Properties();
    p.load( new FileInputStream(file) );
    //...
}

Выходит ли безымянный FileInputStream из области видимости после p.load() и, следовательно, уничтожается, как в правилах области видимости С++? Я попытался найти область анонимных переменных для java в Google, но это не оказалось тем, что я думал.

Спасибо.


person Calyth    schedule 05.10.2009    source источник
comment
Спасибо всем за помощь!   -  person Calyth    schedule 06.10.2009
comment
Обратите внимание, что try с ресурсами в Java 7 или выше сделало бы исправление этой проблемы относительно простой операцией (требуется только одно дополнительное назначение переменной и, конечно же, сам try). Также обратите внимание, что приведенный выше код будет генерировать предупреждение (об отсутствующем закрытии) в моей среде Eclipse.   -  person Maarten Bodewes    schedule 11.02.2016


Ответы (5)


Первый ответ: в Java нет такого понятия, как «разрушение» (в смысле C++). Есть только сборщик мусора, который может проснуться или не проснуться и выполнить свою работу, когда увидит объект, готовый к сбору. Сборщик мусора в Java, как правило, не заслуживает доверия.

Второй ответ: иногда да, иногда нет, но не стоит рисковать. Из Эллиот Расти Гарольд Java IO:

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

По словам Гарольда, то же самое касается входных и выходных потоков. Есть некоторые исключения (он отмечает System.in), но в целом вы рискуете, если не закроете файловые потоки, когда закончите. И закройте их в блоке finally, чтобы убедиться, что они закрыты даже в случае возникновения исключения.

person rtperson    schedule 05.10.2009

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

InputStream stream = null;

try {
  stream = new FileInputStream("bar.txt");
  Properties p = new Properties();
  p.load(stream);
}
catch(Exception e) {
  // error handling
}
finally {
  closeQuietly(stream);
}

closeQuietly() — это метод IOUtils в библиотеке Apache commons-io.

person SingleShot    schedule 05.10.2009

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

Properties.load отличается тем, что он закрывает переданный ему поток. Редактировать: Properties.loadFromXML — это особый метод, о котором я, кажется, думал пять или около того лет назад. (Документация по API, вероятно, должна говорить до, а не после.) Спасибо, @tzimnoch.

person Tom Hawtin - tackline    schedule 05.10.2009
comment
Деструкторов нет, но есть финализаторы. И финализаторы для большинства классов InputStream/OuputStream из java.io закрывают поток. - person ChssPly76; 06.10.2009
comment
@ChssPly76: Верно, но нет никакой гарантии что бы то ни было относительно того, когда или даже будут ли вызваны финализаторы. Никто. - person T.J. Crowder; 06.10.2009
comment
docs.oracle .com/javase/7/docs/api/java/util/ Указанный поток остается открытым после возврата из этого метода. - person tzimnoch; 29.03.2016

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

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

person yfeldblum    schedule 05.10.2009

Короткий ответ: «Может быть, но не ставьте на это!».

Где-то в стеке классов, реализующих FileInputStream, есть класс с finalizer, который эффективно закрывает поток (и освобождает ресурс) при его запуске.

Проблема в том, что нет никакой гарантии, что финализатор когда-либо запустится. Цитата из JLS (раздел 12.6):

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

Это делает завершение потока проблематичным:

  1. Если ваш объект Stream никогда не станет мусором, он никогда не будет завершен.
  2. Если ваш объект Stream является постоянным, может пройти много времени, прежде чем он будет собран и завершен.
  3. JVM может потребоваться выполнить дополнительный цикл GC после того, как объект будет идентифицирован как недостижимый, прежде чем будет выполнен финализатор. Это, безусловно, разрешено JLS!
  4. Для JVM технически законно никогда не выполнять финализаторы, при условии, что она никогда не использует хранилище объектов с финализаторами повторно. (Я не знаю ни о каких JVM производственного качества, которые используют эту линию, но вы никогда не знаете...)
person Stephen C    schedule 06.10.2009
comment
Я думаю, что если у JVM не хватит ресурсов, она завершит работу с закрытыми файловыми объектами. (Не потворствовать тому, чтобы не убирать за собой.) - person mike jones; 15.05.2014
comment
Вы можете подумать, что ... но AFAIK, исчерпание файловых дескрипторов не запускает запуск GC, который обнаружит, что другие потоковые объекты (гипотетически) теперь финализируются. - person Stephen C; 15.05.2014
comment
@mike jones: нет, это не так. Это можно доказать практически. Нехватка памяти может вызвать сборку мусора (она даже обязана сделать все возможное, прежде чем выдать OutOfMemoryError), но gc не идентичен финализации. Сборка мусора приведет к тому, что объекты с финализаторами будут ставиться в очередь для последующей финализации, но совершенно не определено, когда произойдет финализация. Таким образом, при исчерпании файловых дескрипторов (или любого другого ресурса, не связанного с памятью) gc все равно не запускает (как уже указал Стивен), даже если gc запускается, он не вызывает принудительную финализацию. - person Holger; 20.09.2016