日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2023/12/10 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 加载文件流_未关闭的文件流会引起内存泄露么? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

專注于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/

    總結

    以上是生活随笔為你收集整理的加载文件流_未关闭的文件流会引起内存泄露么?的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。