InputStreams будучи GCed - PullRequest
       4

InputStreams будучи GCed

1 голос
/ 14 октября 2010

Я знаю, что если я сделаю что-то вроде

copyFromInToOut(new FileInputStream(f1), new FileOutputStream(f2));
System.gc();

Он будет запускать GC на этих FileInputStream с, закрывая их. Но если я сделаю

copyFromInToOut(new BufferedInputStream(new FileInputStream(f1)), new BufferedOutputStream(new FileOutputStream(f2));
System.gc();

Есть ли какая-либо опасность, что FileOutputStream будет GCed перед BufferedOutputStream, не вызывая сброс буфера? Я не могу вызвать флеш, закрыть, потому что это требует больше шагов, чем это. Сначала нужно объявить буферный поток ввода, передать, а затем вызвать close. ИЛИ Я в безопасности это сделать?

Ответы [ 4 ]

8 голосов
/ 14 октября 2010

Не звоните System.gc() явно. Не полагайтесь на финализаторы, чтобы делать что-либо. Особенно, если вы не понимаете, как работает сборка мусора. Явные запросы на сборку мусора можно игнорировать, и финализаторы могут никогда не запускаться.

Хорошо написанный метод copyFromInToOut для потоков, скорее всего, будет использовать свой собственный буфер для внутреннего использования, поэтому перенос результатов не требуется.

Объявите переменные для FileInputStream и FileOutputStream и вызовите close() для каждого в finally блоке:

FileInputStream is = new FileInputStream(f1);
try {
  FileOutputStream os = new FileOutputStream(f2);
  try {
    copyFromInToOut(is, os);
    os.flush();
  } finally {
    os.close();
  }
} finally {
  is.close();
}
5 голосов
/ 14 октября 2010

Никакая реализация InputStream, с которой я знаю, не будет close() для вас, когда это GCd. Вы ДОЛЖНЫ close() InputStream вручную.

РЕДАКТИРОВАТЬ : Очевидно, FileInputStream действительно закрывает () для вас методом финализации, о котором я не знал, но смотрите другие ответы по той причине, почему вы не должны полагаться на это.

В обоих приведенных выше примерах вы должны закрыть оба потока ввода и вывода. Для упакованного буфера и для любого упакованного вам нужно только вызвать close() для самого внешнего InputStream, в данном случае BufferedInputStream

2 голосов
/ 14 октября 2010

Потоки должны быть явно закрыты с использованием шаблона, показанного в ответе @ erickson.Полагаться на завершение закрытия потоков для вас - это очень плохая идея :

  1. Вызов System.gc() стоит дорого, тем более что (если он что-то делает) с большой вероятностьюзапустить полную сборку мусора.Это приведет к отслеживанию каждой ссылки в каждом достижимом объекте в вашей куче.

  2. Если вы прочитаете javadocs для System.gc(), вы увидите, что это только«подсказка» JVM для запуска GC.JVM может игнорировать подсказку ... что приводит нас к следующей проблеме.

  3. Если вы не запустите GC явно, это может занять много времени, пока GCпробеги.И даже тогда нет никакой гарантии, что финализаторы будут запущены немедленно.

В то же время:

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

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

EDIT

Возвращаясь к исходному вопросу, оказывается, что класс BufferedOutputStream не переопределяет метод Object.finalize() по умолчанию.Это означает, что BufferedOutputStrean вообще не сбрасывается при сборке мусора.Любые неписанные данные в буфере будут потеряны.

Это еще одна причина явного закрытия ваших потоков.В самом деле, в данном конкретном случае вызов System.gc() - это не просто плохая практика;также вероятно может привести к потере данных.

2 голосов
/ 14 октября 2010

ну, интересно проанализировать, что на самом деле происходит, когда вы делаете это, просто не делайте этого.

всегда закрывайте свои потоки явно.

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

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