专注于Java领域优质技术,欢迎关注

来自:技术小黑屋

最近接触了一些面试者,在面试过程中有涉及到内存泄露的问题,其中有不少人回答说,如果文件打开后,没有关闭会导致内存泄露。当被继续追问,为什么会导致内存泄露时,大部分人都没有回答出来。

本文将具体讲一讲 文件(流)未关闭与内存泄露的关系。

什么是内存泄露

  • 定义:当生命周期长的实例L 不合理地持有一个生命周期短的实例S,导致S实例无法被正常回收

举例说明

上面的代码可能会发生内存泄露

  • 我们调用AppSettings.getInstance.setup()传入一个Activity实例
  • 当上述的Activity退出时,由于被AppSettings中属性mAppContext持有,进而导致内存泄露。

为什么上面的情况就会发生内存泄露

  • 以 Android 为例,GC 回收对象采用GC Roots强引用可到达机制。
  • Activity实例被AppSettings.sInstance持有
  • AppSettings.sInstance由于是静态,被AppSettings类持有
  • AppSettings类被加载它的类加载器持有
  • 而类加载器就是GC Roots的一种
  • 由于上述关系导致Activity实例无法被回收销毁。

验证是否引起内存泄露

因此,想要证明未关闭的文件流是否导致内存泄露,需要查看文件流是否是GC Roots强引用可到达。

示例代码1(辅助验证GC 发生)

示例代码2

这里我们这样操作

  1. 点击textview视图,触发多次testInputStream
  2. 过几秒后,我们执行heap dump。
  3. 我们使用 MAT 对上一步的dump文件进行分析(需进行格式转换)

分析上图,我们发现

  • FileInputStream 只被 FinalizerReference 这个类(GC Root)持有
  • 上述持有的原因是,FileInputStream重写了finalize,会被加入到FinalizerReference的析构处理集合
  • 上述引用会随着Finalizer守护线程处理后解除,即FileInputStream实例彻底销毁。

所以,我们再来操作一波,验证上面的结论。

  • 然后利用工具执行强制GC回收
  • 过几秒后,我们执行heap dump。
  • 我们使用 MAT 对上一步的dump文件进行分析(需进行格式转换)
  • 堆分析文件,查找MyBufferedReader或者FileInputStream或者InputStreamReader 没有发现这些实例,说明已经GC回收
  • 出于谨慎考虑,我们按照包名查找java.io在排除无关实例外,依旧无法找到testInputStream中的实例。再次证明已经被GC回收

因而我们可以确定,正常的使用流,不会导致内存泄露的产生。

当然,如果你刻意显式持有Stream实例,那就另当别论了。

为什么需要关闭流

首先我们看一张图

如上图从左至右有三张表

  • file descriptor table 归属于单个进程
  • global file table(又称open file table) 归属于系统全局
  • inode table 归属于系统全局

从一次文件打开说起

当我们尝试打开文件/path/myfile.txt

1.从inode table 中查找到对应的文件节点

2.根据用户代码的一些参数(比如读写权限等)在open file table 中创建open file 节点

3.将上一步的open file节点信息保存,在file descriptor table中创建 file descriptor

4.返回上一步的file descriptor的索引位置,供应用读写等使用。

file descriptor 和流有什么关系

  • 当我们这样FileInputStream("/sdcard/a.txt") 会获取一个file descriptor。
  • 出于稳定系统性能和避免因为过多打开文件导致CPU和RAM占用居高的考虑,每个进程都会有可用的file descriptor 限制。
  • 所以如果不释放file descriptor,会导致应用后续依赖file descriptor的行为(socket连接,读写文件等)无法进行,甚至是导致进程崩溃。
  • 当我们调用FileInputStream.close后,会释放掉这个file descriptor。

因此到这里我们可以说,不关闭流不是内存泄露问题,是资源泄露问题(file descriptor 属于资源)。

不手动关闭会怎样

不手动关闭的真的会发生上面的问题么? 其实也不完全是。

因为对于这些流的处理,源代码中通常会做一个兜底处理。以FileInputStream为例

是的,在finalize方法中有调用close来释放file descriptor.

但是finalize方法执行速度不确定,不可靠

所以,我们不能依赖于这种形式,还是要手动调用close来释放file descriptor。

关闭流实践

Java 7 之后,可以使用try-with-resource方式处理

Kotlin 可以使用use

当然,还有最基础的手动关闭的形式

Reference

  • https://stackoverflow.com/questions/26541513/why-is-it-good-to-close-an-inputstream
  • https://www.reddit.com/r/learnjava/comments/577769/why_do_you_need_to_close_streams/

来自:https://droidyue.com/blog/2019/06/09/will-unclosed-stream-objects-cause-memory-leaks/

加载文件流_未关闭的文件流会引起内存泄露么?相关推荐

  1. workbook加载文件路径_【Python】文件重命名(按照Excel清单)

    问题: 1.扫描了很多文件,想批量加上文件编号 2.网页下载了一堆图片,批量加上图片分类 3.文件命名不规范,想批量调整 效果图: 需求解析: 1.想重命名,就要有原名字和修改后的名字,清单准备好: ...

  2. workbook加载文件路径_通过Workbook.XML 修复Excel自定义名称

    小伙伴们经常想求助IT 提升打开Excel的速度, 标准回答是:重启. 其实Excel 中影响打开速度的几个因素: 1. 公式 2. 链接 3. 自定义名称  自定义名称常常被人忽视,里面经常隐藏着众 ...

  3. dotnet安装包时找不到依赖关系_无法加载文件或程序集'Microsoft.AspNet.TelemetryCorrelation'或其依赖项之一 . 该系统找不到指定的文件...

    我在IIS 7(本地Windows 10 Pro)下有两个Web应用程序 . 根目录是MVC .Net Version 4.7.1 . 到目前为止,一切似乎都很好 . 但是,在默认站点根目录下设置为单 ...

  4. c# 未能加载文件或程序集mysql.data,c# - 当我未引用文件或程序集Oracle.ManageddataAccess时,它无法加载 - 堆栈内存溢出...

    我不断 System.IO.FileNotFoundException:'无法加载文件或程序集'Oracle.ManagedDataAccess,Version = 4.122.1.0,Culture ...

  5. C#错误:CS0012 未能加载文件或程序集“netstandard, Version=2.0.0.0, Culture=neutral解决方案

    错误如题所示,找了很多解决方案都没用,最后看到一个帖子,结合自己的实际情况进一步研究终于解决,将解决过程记录在这里. (1).NET Framework版本调整 这部分受下面链接启发: [.NET S ...

  6. 未能加载文件或程序集“Antlr3.Runtime”或它的某一个依赖项。参数错误。 (异常来自 HRESULT:0x80070057 (E_INVALIDARG))解决方法。...

    前一天晚上VS卡死,强制关闭后就没理他,然后晚上回去又经历了一次过热断电关机和一次蓝屏,当然我也不知道这些和他有没有关联,第二天早上打开程序出现了:"未能加载文件或程序集"Antl ...

  7. Powershell / windows终端 无法加载文件 因为在此系统上禁止运行脚本。有关详细信息,请参阅 https:/go.microsoft.com/fwlink/?LinkID=135170

    问题描述 在 powershell 或者 windows 终端中运行脚本文件时,提示"无法加载文件 因为在此系统上禁止运行脚本.有关详细信息,请参阅 https:/go.m icrosoft ...

  8. AE开发 遇到未能加载文件或程序集的问题 FileNotFoundException

    AE 开发软件 遇到: 未处理FileNotFoundException 未能加载文件或程序集 "xxx, version = 1.0.0.0, cultrue = neutral, Pub ...

  9. 未能加载文件或程序集“XXX.dll”或它的某个依赖项的解决方法

    问题: 昨天从开发机拷贝exe程序到测试机上测试时,发现程序一运行就报以下错误:未能加载文件或程序集"A.dll"或它的某一个依赖项.找不到指定的模块. 相关知识: 1.应用程序查 ...

最新文章

  1. Huggingface及BERT代码介绍
  2. Mybatis的第三章动态sql总结
  3. linux cp和rm命令
  4. 基于图像切换器(imageSwitcher)的支持动画的图片浏览器
  5. 解决CsrfFilter与Rest服务Post方式的矛盾
  6. 世行报告称气候变暖已给发展中国家造成影响
  7. spring入门常见的问题及解决办法
  8. JSON字符串与Map互转
  9. shell脚本一键安装jdk
  10. c语言语法——c语言时间类型
  11. Mysql和ES数据同步方案汇总
  12. 用C#获取系统中的临时文件夹或windows安装文件夹
  13. CVPR 2022 Oral | 人大高瓴AI学院提出:面向动态视音场景的问答学习机制
  14. c语言程序(十八)——迭代计算
  15. Ubuntu 16.04 LTS 初体验 (转载)
  16. 可视门铃竟然可以泄露WiFi密码
  17. mysql中如何查看表结构
  18. FreeRTOS内核——任务与任务切换
  19. 做自动化用哪种开发语言,一起【探讨】,谈谈你的想法?
  20. Facebook分享使用

热门文章

  1. [css] 你对响应式设计的理解是什么?知道它基本的原理是吗?要想兼容低版本的IE怎么做呢?
  2. [js] 微信的JSSDK都有哪些内容?如何接入?
  3. 前端学习(2682):重读vue电商网站3之登录页面总结el-form 组件问题
  4. 工作137:map函数
  5. spring学习(45):util名称空间注入
  6. 树莓派移植SX1278 LoRa通信--使用wiringPiSPI移植SPI通信接口
  7. java文件全是数字编码_批量将Java源代码文件的编码从GBK转为UTF-8
  8. java语言中的 继承_Java语言有关继承的总结
  9. solor mysql_solr 同步 mysql
  10. 机器学习算法总结--决策树