加载文件流_未关闭的文件流会引起内存泄露么?
專注于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
這里我們這樣操作
分析上圖,我們發現
- 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/
總結
以上是生活随笔為你收集整理的加载文件流_未关闭的文件流会引起内存泄露么?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Multisim 版本针对3D元件库说明
- 下一篇: java请求注释_求达人给java代码【