Влияют ли входные/выходные потоки на разрушение?

Использует ли InputStreams и OutputStreams в Java close() при уничтожении? Я полностью понимаю, что это может быть плохая форма (esp в мире C и С++), но мне любопытно.

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

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

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

Спасибо.

Ответ 1

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

Второй ответ: иногда да, иногда нет, но не стоит рисковать. Из Elliote Rusty Harold Java IO:

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

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

Ответ 2

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

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.

Ответ 3

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

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

Ответ 4

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

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

Ответ 5

Короткий ответ "возможно, но не делайте ставку на него!".

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

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

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

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

  • Если ваш объект Stream никогда не станет мусором, он никогда не будет завершен.
  • Если ваш объект Stream занят, он может занять много времени, прежде чем он будет собран и завершен сбор мусора.
  • JVM, возможно, потребуется выполнить дополнительный цикл GC после того, как объект будет идентифицирован как недостижимый до выполнения финализатора. Это, безусловно, разрешено JLS!
  • Технически законно для JVM никогда, никогда не выполнять финализаторы, при условии, что он никогда не повторяет хранилище объектов с финализаторами. (Я не знаю о каких-либо JVM качества продукции, которые берут эту строку, но вы никогда не знаете...)