Does JVM automatically closes files?

前端 未结 2 441
眼角桃花
眼角桃花 2021-01-16 04:59

Somewhere I read that it\'s unnecessary to close it by yourself, just leave it, JVM will help you do this. Is it true?

Assume I need to fetch data from file with

相关标签:
2条回答
  • 2021-01-16 06:02

    Yes, In some point it's true, JVM will help release the file descriptor when GC in some time with invoking finalize method(you can try to verify it by System.gc()).

    And also totally agree we need to explicitly close InputStream when we don't need this and release the file descriptor.

    but if your application don't frequently operate on file descriptors(file, network io), do we need to care this too? after all, there is finalize method.

    Yes it's a good practice to close it, explicitly release it.

    and for OP's problem is very like Java's: how to safely close the Stream by Files.lines. In Java, we can use the try with resources handle this. But Scala not support this, maybe you can try with try ... finally to handle this, like:

    val reader = Source.fromFile("build.sbt").bufferedReader()
    try {
      reader.lines().forEach(line => {
      ...
      })
    } finally {
      reader.close()
    }
    
    0 讨论(0)
  • 2021-01-16 06:03

    Somewhere I read that it's unnecessary to close it by yourself, just leave it, JVM will help you do this. Is it true?

    It is partly true.

    If you open a file, use it and then drop the file or stream handle (or whatever you want to call it), AND the GC finds it, THEN the GC will queue the file handle object for finalization. When the finalization occurs the file handler's finalize() method will release the resources; i.e. the file descriptor.

    However, it is a bad idea to rely on the GC to do this.

    • If a file handle is reachable, then the GC won't finalize it.
    • You have of way of knowing when the GC is going to run next1.
    • And when the GC runs, it won't necessarily collect all of the garbage2.
    • And objects that are queued for finalization don't get actually finalized until after the GC finishes. (See @Holger's comment)

    Put these four things together, and an application can easily run out of file descriptors before the GC gets around to collecting and closing the abandoned file handles. If that happens, you are liable to get exceptions on operations that open files, directories, sockets, etcetera.

    Here's an example you can run (on Linux / UNIX) to see this happening:

    import java.io.FileInputStream;
    
    public class Test {
        public static void main(String[] args) throws Exception {
            for (int i = 0; i < 100000; i++) {
                new FileInputStream("/etc/motd");
            }
        }
    }
    
    $ javac Test.java 
    $ java Test 
    Exception in thread "main" java.io.FileNotFoundException: /etc/motd (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 Test.main(Test.java:6)
    

    1 - A typical GC only runs when the heap (or part of the heap) reaches a given threshold of "fullness". If an application doesn't allocate many objects, it can take a long time for the threshold to be reached.

    2 - Modern JVMs use generational garbage collectors which collect different parts of the heap at different rates.

    0 讨论(0)
提交回复
热议问题