加载文件流_未关闭的文件流会引起内存泄露么?
专注于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
这里我们这样操作
- 点击textview视图,触发多次testInputStream
- 过几秒后,我们执行heap dump。
- 我们使用 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/
加载文件流_未关闭的文件流会引起内存泄露么?相关推荐
- workbook加载文件路径_【Python】文件重命名(按照Excel清单)
问题: 1.扫描了很多文件,想批量加上文件编号 2.网页下载了一堆图片,批量加上图片分类 3.文件命名不规范,想批量调整 效果图: 需求解析: 1.想重命名,就要有原名字和修改后的名字,清单准备好: ...
- workbook加载文件路径_通过Workbook.XML 修复Excel自定义名称
小伙伴们经常想求助IT 提升打开Excel的速度, 标准回答是:重启. 其实Excel 中影响打开速度的几个因素: 1. 公式 2. 链接 3. 自定义名称 自定义名称常常被人忽视,里面经常隐藏着众 ...
- dotnet安装包时找不到依赖关系_无法加载文件或程序集'Microsoft.AspNet.TelemetryCorrelation'或其依赖项之一 . 该系统找不到指定的文件...
我在IIS 7(本地Windows 10 Pro)下有两个Web应用程序 . 根目录是MVC .Net Version 4.7.1 . 到目前为止,一切似乎都很好 . 但是,在默认站点根目录下设置为单 ...
- c# 未能加载文件或程序集mysql.data,c# - 当我未引用文件或程序集Oracle.ManageddataAccess时,它无法加载 - 堆栈内存溢出...
我不断 System.IO.FileNotFoundException:'无法加载文件或程序集'Oracle.ManagedDataAccess,Version = 4.122.1.0,Culture ...
- C#错误:CS0012 未能加载文件或程序集“netstandard, Version=2.0.0.0, Culture=neutral解决方案
错误如题所示,找了很多解决方案都没用,最后看到一个帖子,结合自己的实际情况进一步研究终于解决,将解决过程记录在这里. (1).NET Framework版本调整 这部分受下面链接启发: [.NET S ...
- 未能加载文件或程序集“Antlr3.Runtime”或它的某一个依赖项。参数错误。 (异常来自 HRESULT:0x80070057 (E_INVALIDARG))解决方法。...
前一天晚上VS卡死,强制关闭后就没理他,然后晚上回去又经历了一次过热断电关机和一次蓝屏,当然我也不知道这些和他有没有关联,第二天早上打开程序出现了:"未能加载文件或程序集"Antl ...
- Powershell / windows终端 无法加载文件 因为在此系统上禁止运行脚本。有关详细信息,请参阅 https:/go.microsoft.com/fwlink/?LinkID=135170
问题描述 在 powershell 或者 windows 终端中运行脚本文件时,提示"无法加载文件 因为在此系统上禁止运行脚本.有关详细信息,请参阅 https:/go.m icrosoft ...
- AE开发 遇到未能加载文件或程序集的问题 FileNotFoundException
AE 开发软件 遇到: 未处理FileNotFoundException 未能加载文件或程序集 "xxx, version = 1.0.0.0, cultrue = neutral, Pub ...
- 未能加载文件或程序集“XXX.dll”或它的某个依赖项的解决方法
问题: 昨天从开发机拷贝exe程序到测试机上测试时,发现程序一运行就报以下错误:未能加载文件或程序集"A.dll"或它的某一个依赖项.找不到指定的模块. 相关知识: 1.应用程序查 ...
最新文章
- Huggingface及BERT代码介绍
- Mybatis的第三章动态sql总结
- linux cp和rm命令
- 基于图像切换器(imageSwitcher)的支持动画的图片浏览器
- 解决CsrfFilter与Rest服务Post方式的矛盾
- 世行报告称气候变暖已给发展中国家造成影响
- spring入门常见的问题及解决办法
- JSON字符串与Map互转
- shell脚本一键安装jdk
- c语言语法——c语言时间类型
- Mysql和ES数据同步方案汇总
- 用C#获取系统中的临时文件夹或windows安装文件夹
- CVPR 2022 Oral | 人大高瓴AI学院提出:面向动态视音场景的问答学习机制
- c语言程序(十八)——迭代计算
- Ubuntu 16.04 LTS 初体验 (转载)
- 可视门铃竟然可以泄露WiFi密码
- mysql中如何查看表结构
- FreeRTOS内核——任务与任务切换
- 做自动化用哪种开发语言,一起【探讨】,谈谈你的想法?
- Facebook分享使用
热门文章
- [css] 你对响应式设计的理解是什么?知道它基本的原理是吗?要想兼容低版本的IE怎么做呢?
- [js] 微信的JSSDK都有哪些内容?如何接入?
- 前端学习(2682):重读vue电商网站3之登录页面总结el-form 组件问题
- 工作137:map函数
- spring学习(45):util名称空间注入
- 树莓派移植SX1278 LoRa通信--使用wiringPiSPI移植SPI通信接口
- java文件全是数字编码_批量将Java源代码文件的编码从GBK转为UTF-8
- java语言中的 继承_Java语言有关继承的总结
- solor mysql_solr 同步 mysql
- 机器学习算法总结--决策树