Java中的FileInputStream是否需要close问题
目录
- FileInputStream 类简介
- FileInputStream 的 finalize() 方法
- 实际测试
- 结论
- 会有其他问题吗
- 主动 close 的方式
- 总结
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. * * @excep编程tion 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); } /** * 重写了 finalize 方法,把父类的 finalize 中的方法复制到此处,并且在其中添加打印信息 * * @throws IOException 异常 */ @Override protected void finalize() throws IOException { if ((this.getFD() != null) && (this.getFD() != 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(); System.out.println("finalize and close"); } else { System.out.println("only call finalize"); } } }
可以看到,只要执行了 finalize() 方法,那么就会打印一行 “finalize”。
然后新建测试类,如下代码:
import java.io.File; import java.io.IOException; import java.util.concurrent.TimeUnit; /** * 测试 MyFileInputStream 的 Finalize * * @author slee编程客栈pybear - 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 = org.apache.commons.codec.digest.DigestUtils.md5Hex(new MyFileInputStream(file)); System.out.println("count = " + count + ", md5 = " + md5); } catch (IOException e) { e.printStackTrace(); } } } } }
运行代码,得到如下的结果:
count = 2000, md5 = da6a56cda0772e7b321621472f1ca9ce
count = 4000, md5 = 8fc19cf5f7675179ed14abce784e29dacount = 6000, md5 = f93186d553b72e168f84ef9870285a17count = 8000, md5 = 6af44868883a83b7ae4a0c3529ebc6efcount = 10000, md5 = f570fdda290a62db840538c460e46510count = 12000, md5 = 26ae171433b7c355223fa43f9d605110count = 14000, md5 = c5f924cee8c51c19825af3713044b67acount = 16000, md5 = deda72e7ef14674a49295f460301b4cfcount = 18000, md5 = 08753370d8c5bbda239e4d349892730ccount = 20000, md5 = df1213e1584803bf0a549c5a1c14f111count = 22000, md5 = 9751d0dbc67c75cb446bdaf2d2434f66count = 24000, md5 = 962cf50af21894734a78493e2f4df31bcount = 26000, md5 = f9556c74d758838e9c2202921de50056count = 28000, md5 = 2a2699c13ff46dd34305bc5c7b780b52count = 30000, md5 = 71af55db4adf6c7b2e667f031a809e91count = 32000, md5 = bdef65ff9a12c5808b491f4b0be362d1count = 34000, md5 = 9f1da8e150bfe5077c7ab82b3727aba0count = 36000, md5 = 648422e1e6b89a1e331e407e6c7fc652count = 38000, md5 = d1d9e7a656db7d0a4e79110fdb3f3462count = 40000, md5 = 50b6c156bf30447d4f0b891677b75617count = 42000, md5 = 1be6de12ec79e310675f1b1e5f1e891ccount = 44000, md5 = 027ca2c40a3d9b2d8f7818673cb6329ccount = 46000, md5 = 07e1a13fc5e3e5fdd3cacf998e57eaa8count = 48000, md5 = c3bf74579b053ccdd5bb6bed7c8c5ab1count = 50000, md5 = 78a2a70250a4df4f21523e0b94d6bca4count = 52000, md5 = 769f5ea0d0a2c2b89d82a2bf2dbdbeddcount = 54000, md5 = c092d2f664c726da95f058018134bdfbcount = 56000, md5 = dc4d6f6ac6212f91d0aba93d692564c4count = 58000, md5 = 217926c75b000f1dea20d319e9aeebbfcount = 60000, md5 = b437b7e80f6c52a42c3e4fe74be49c48count = 62000, md5 = 9a92a6cf85e5513550ab801e12867dc9count = 64000, md5 = b92bc3f149a121039aa8fe6a05f47b35count = 66000, md5 = 064fd7ca2040cb21615e205b1f294e19count = 68000, md5 = b4d20b20526769ef73702108c519cf25count = 70000, md5 = 2436edd2550c69c1c2a519edfee2b853finalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closecount = 72000, md5 = 6513a7abfe9f456814216f4040d82f2ecount = 74000, md5 = 7b1a34f38c218fa59b775543e1d2784fcount = 76000, md5 = 5e02f191cec09d5a938233dd43bec96dcount = 78000, md5 = 8e80d1b1e0c42af8937fc570e94145d4count = 80000, md5 = 42c2b3d42c7966e47a1de23e3c89eca6count = 82000, md5 = ce45fc5afea20d7e5a4fc79ddc3b04cbcount = 84000, md5 = 51bd1280db5b549968b855f7d25f7075count = 86000, md5 = c90629033ec6b8fbfe5f17f63a44cfa3count = 88000, md5 = 71b5e1bdc7f76444fb0fe88bc0e3a669count = 90000, md5 = a930f1957f273fd5cb0d7fc984096ce4count = 92000, md5 = badfe45e2c5917dcec2d0be1a03583decount = 94000, md5 = 9e74b89c1f8e8ecfb00b84001b45ddd7count = 96000, md5 = 7b4464b79280f9ac23e4b241508aa2d1count = 98000, md5 = d79fabe6804056b0a6572e95f7c970c0count = 100000, md5 = bcb8f86c91f8e106bdce40584ddba54bcount = 102000, md5 = 8f578f7a6dbcbde77d375a2e7c98ceeecount = 104000, md5 = aa39503815c583a38b409c92e04e5d14count = 106000, md5 = 34ec7897529f1b00a78b13DB223f907bcount = 108000, md5 = ea83e11ece4e7fdcc23bd9214f486ae3count = 110000, md5 = 05d69a87ebf4133795aad1aae4a99ebbcount = 112000, md5 = bcc781b71ff6b10a57923af9fcc85b38count = 114000, md5 = 5e468c6233db3f6a4311ebafa6e35eb6count = 116000, md5 = 365a5b3af1dd522ed7c9a8b106f90224count = 118000, md5 = 9117c65ed9ff083256a86af10ab88d65count = 120000, md5 = 97f24779279cfe2fa1d573ef83c21009count = 122000, md5 = ba6b12f0b76b192d9dd74965919bad95count = 124000, md5 = b54c8105da76b3f13dcf78495bc2ff52count = 126000, md5 = 648422e1e6b89a1e331e407e6c7fc652count = 128000, md5 = eb115942b0abf38b881353debe4bdb92count = 130000, md5 = e469a50bf7cfd9f1fb95f7d5badc1641count = 132000, md5 = 2be1bd409def8cfc4f32a49af4bf5e9fcount = 134000, md5 = e3a20ac5df9d81a2ce301886fdfc0f07count = 136000, md5 = 77269649674cc0fdf4b379a523a3a829count = 138000, md5 = aead3fc8f94编程5488ab71cf64271f46c54count = 140000, md5 = e7098eafd97649e4a77edea89d0d22abcount = 142000, md5 = 28d392b26bbd86fb3312960b82572060count = 144000, md5 = f37ea0c388bec909255c602adc0cdfe4count = 146000, md5 = d91659c455a4f66f9a16c8f287ce9fc9count = 148000, md5 = 338364a0d43b018333d3143a466e1cf2遍历结束,等待 2 秒显式调用 gcfinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and cl开发者_Python培训osefinalize and closefinalize and closefinalhttp://www.devze.comize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefwww.devze.cominalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and closefinalize and close
结论
上述测试可以看到,即使没有主动 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(); }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。
精彩评论