Почему необходимо закрывать соединения с файлами в Java? - PullRequest
0 голосов
/ 09 апреля 2019

Я прочитал, что этот файл является неуправляемым ресурсом, и сборщик мусора о нем не позаботится.

Если вы не закроете файл, я уверен, что ссылка на файл будет собираться мусором, если на него ничего не ссылается. Так что же остается открытым? Это что-то на уровне операционной системы? Как и для соединений SQL, я знаю, что ОС поддерживает открытый порт TCP, и у вас могут закончиться порты. Но что остается открытым в случае файла?

Ответы [ 2 ]

4 голосов
/ 09 апреля 2019

Сборщик мусора может со временем высвободить ресурсы ОС, благодаря методу finalize() в классах, которые обертывают ресурсы. Однако освобождение ресурсов ОС в какой-то момент в будущем недостаточно.

В частности, есть две проблемы:

  1. Вы можете достичь предела ОС задолго до того, как GC сможет запустить. Достижение такого предела не запускает автоматический сборщик мусора, как при исчерпании пространства кучи.
  2. Даже если вы освободите ресурс ОС, у вас могут остаться буферы уровня приложения, которые не будут очищены.

Например, Debian Linux имеет ограничение по умолчанию на количество открытых файлов 1024, чтобы программа DoS не работала неправильно. Рассмотрим эту программу, которая оптимально должна использовать только один FD на одну итерацию:

import java.io.*;
class Foo {
  public static void main(String[] args) throws Exception {
    for(int i=0; i<2000; i++) {
      FileInputStream fis = new FileInputStream("Foo.java");
    }
  }
}

Вот что происходит, когда вы запускаете его:

$ java Foo
Exception in thread "main" java.io.FileNotFoundException: Foo.java (Too many open files)
        at java.io.FileInputStream.open0(Native Method)
        at java.io.FileInputStream.open(FileInputStream.java:195)
        at java.io.FileInputStream.<init>(FileInputStream.java:138)
        at java.io.FileInputStream.<init>(FileInputStream.java:93)
        at Foo.main(Foo.java:5)

Если бы вы закрыли файл вручную, этого бы не произошло.

Вот еще один пример программы, которая записывает строку в файл, а затем считывает ее обратно:

import java.io.*;
class Foo {
  static void writeConfig(String s) throws IOException {
    BufferedWriter fw = new BufferedWriter(new FileWriter("config.txt"));
    fw.write(s);
    System.out.println("Successfully wrote config");
  }
  static String readConfig() throws IOException {
    BufferedReader reader = new BufferedReader(new FileReader("config.txt"));
    return reader.readLine();
  }
  public static void main(String[] args) throws Exception {
    writeConfig("Hello World");
    System.gc();  // Futile attempt to rely on the GC
    String input = readConfig();
    System.out.println("The config string is: " + input);
  }
}

Вот что вы получите:

$ java Foo
Successfully wrote config
The config string is: null

Записанная строка не попала в файл. Если вы закрыли BufferedWriter, это не будет проблемой.

0 голосов
/ 09 апреля 2019

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

Однако рекомендуется закрыть его, потому что если у вас много таких случаев, когда файлы остаются открытыми, у вас есть риск нехватки памяти, если естьгенерируется достаточное количество потоков, которые открывают файлы, но вы не закрываете их - в конечном счете, достигаете максимального размера памяти JVM до того, как включится GC.

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