0
点赞
收藏
分享

微信扫一扫

Java 的 FileInputStream 是否需要 close

大南瓜鸭 2022-01-09 阅读 124

目录

FileInputStream 类简介

FileInputStream 类在 Java 中非常常用,用来读取文件流的。而这种读取方式往往会涉及到流的关闭 close。
如果不关闭 FileInputStream 流会有问题吗?会导致内存泄漏吗?

FileInputStream 的 finalize() 方法

Java 中每个 Object 都有 finalize() 方法,用来在 gc 的时候调用。而 FileInputStream 类中的 finalize() 方法中有一点特别的,它重写了这个方法,并且在里面进行了 close()。

如下代码:

    /**
     * Ensures that the <code>close</code> method of this file input stream is
     * called when there are no more references to it.
     *
     * @exception  IOException  if an I/O error occurs.
     * @see        java.io.FileInputStream#close()
     */
    protected void finalize() throws IOException {
        if ((fd != null) &&  (fd != FileDescriptor.in)) {
            /* if fd is shared, the references in FileDescriptor
             * will ensure that finalizer is only called when
             * safe to do so. All references using the fd have
             * become unreachable. We can call close()
             */
            close();
        }
    }

可以看到,只要触发了 gc,那么就会调用 finalize() 方法,那么就会自动 close 流。当被 close 之后,也就不会发生内存泄漏了。

那么,不主动关闭,并且不主动触发 System.gc() 的话,它会被 JVM 回收吗?

实际测试

为了更加直观地看到是否调用了 finalize() 方法,这里新建一个 MyFileInputStream 类 extends FileInputStream,为的是重写 FileInputStream 的 finalize() 方法,给里面加入一行打印输出。

代码如下:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * a class extends FileInputStream to test method finalize()
 *
 * @author sleepybear - https://blog.csdn.net/qq_36670734
 * @date 2022/1/9 21:02
 */
public class MyFileInputStream extends FileInputStream {
    public MyFileInputStream(File file) throws FileNotFoundException {
        super(file);
    }

    @Override
    protected void finalize() throws IOException {
        System.out.println("finalize");
        super.finalize();
    }
}

可以看到,只要执行了 finalize() 方法,那么就会打印一行 “finalize”。

然后新建测试类,如下代码:

import org.apache.commons.codec.digest.DigestUtils;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * 测试 MyFileInputStream 的 Finalize
 *
 * @author sleepybear - https://blog.csdn.net/qq_36670734
 * @date 2022/1/9 21:10
 */
public class TestFinalize {
    /**
     * 计数
     */
    public static int count = 0;

    public static void main(String[] args) throws InterruptedException {
        listFiles("D://Work//");
        System.out.println("遍历结束,等待 2 秒");
        TimeUnit.MILLISECONDS.sleep(1000 * 2L);
        System.out.println("显式调用 gc");
        System.gc();
        TimeUnit.MILLISECONDS.sleep(1000 * 2L);
        System.out.println("结束");
    }

    /**
     * 递归遍历所有文件,若遍历到 2000 的整数倍,那么输出这个文件的 md5
     *
     * @param path 路径
     */
    public static void listFiles(String path) {
        if (path == null || path.length() == 0) {
            return;
        }

        File file = new File(path);
        if (!file.exists()) {
            return;
        }

        if (file.isDirectory()) {
            // 遇到文件夹,往里面继续递归
            File[] files = file.listFiles();
            if (files != null && files.length > 0) {
                for (File dir : files) {
                    listFiles(dir.getAbsolutePath());
                }
            }
        }

        if (file.isFile()) {
            // 遇到是文件,那么计数 +1
            count++;
            if (count % 2000 == 0) {
                // 如果是 2000 的整数倍,那么打印文件的 md5,md5 打印需要用到 commons-codec-1.15.jar
                try {
                    // 这里直接 new MyFileInputStream 并没有显式 close,同时工具方法里面也没有调用 close()
                    String md5 = DigestUtils.md5Hex(new MyFileInputStream(file));
                    System.out.println("count = " + count + ", md5 = " + md5);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

运行代码,得到如下的结果:

结论

上述测试可以看到,即使没有主动 close 流,MyFileInputStream 类(或者是 FileInputStream 类)在结束之后,也会自动被回收。在回收的时候调用 finalize() 方法自行 close 流。

所以在使用 FileInputStream 类,不显式调用 close 方法的话,一般不会造成内存泄漏。

会有其他问题吗

以下结论参考自下述文章

不主动 close 流的话,文件句柄还占用着,如果 JVM 没有及时清理这些流的话,那么可能导致资源泄露问题,可能会因为过多打开文件导致 CPU 和 RAM 占用居高,甚至无法再打开新的文件进行读写。

主动 close 的方式

    FileInputStream fileInputStream = null;
    try {
        fileInputStream = new FileInputStream(file);
        fileInputStream.read();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (fileInputStream != null) {
            fileInputStream.close();
        }
    }

当 Java 1.7 之后,可以使用 try-with-resources 方式自动 close 流

    try (FileInputStream fileInputStream = new FileInputStream(file)) {
        fileInputStream.read();
    } catch (IOException e) {
        e.printStackTrace();
    }
举报

相关推荐

0 条评论