亚洲少妇一区二区三区_精品成人无码一区二区三区_日韩影院一区二区_a天堂视频在线观看_草草影院第一页_香蕉视频污视频_99久久国产精_91成人在线观看喷潮蘑菇_天堂www中文在线资源_精品无码人妻一区二区免费蜜桃_国产黑丝一区二区

當前位置:首頁 > 今日熱點 > 民生資訊 > 正文

IO流為什么必須手動關(guān)閉,不能像其他的對象坐等GC回收?

2023-07-07 09:32:43    來源:Java極客技術(shù)    
一、問題回溯

在項目的開發(fā)過程中,當我們對文件進行讀寫操作時,不知道大家有沒有碰到這樣的問題。

有的同學(xué)在做一個讀取臨時文件數(shù)據(jù)的工作,當讀完文件內(nèi)容,準備將其刪除的時候,有時候會正常,但有時候會提示:操作無法完成,因為文件已在 Java? Platform SE binary 中打開,編譯器也會提示:Resource leak: "xxxx" is never closed。


(資料圖)

樣例代碼如下:

File file = new File("xxx.txt");// 實例化輸入流FileReader reader = new FileReader(file);// 緩沖區(qū)char[] buffer = new char[1024];// 分次讀取數(shù)據(jù),每次最多讀取1024個字符,將數(shù)據(jù)讀取到緩沖區(qū)之中,同時返回讀取的字節(jié)個數(shù)int len;while ((len = reader.read(buffer)) > -1) {    // 字符轉(zhuǎn)為字符串    String msg = new String(buffer, 0, len);    System.out.println(msg);}// 刪除文件file.delete();

經(jīng)過排查,發(fā)現(xiàn)出現(xiàn)該問題的原因是:讀取文件的 IO 流沒有正常的關(guān)閉,導(dǎo)致文件一直被流持有,刪除文件不成功!

那這么解決這個問題呢?答案其實也很簡單,當讀完 IO 流的數(shù)據(jù)或者寫完數(shù)據(jù),手動調(diào)用一下關(guān)閉流的方法,最后再進行刪除文件。

// 刪除文件之前,先將 IO 流關(guān)閉reader.close();// 刪除文件file.delete();

可能有的同學(xué)會發(fā)出疑問,為什么 IO 流必須手動關(guān)閉,不能像其他的方法一樣坐等 GC 回收?

今天我們就一起來聊聊這個話題,以及如何正確的關(guān)閉 IO 流操作。

二、為什么 IO 流需要手動關(guān)閉?

熟悉編程語言的同學(xué),可能知道,無論是 C 語言還是 C++,都需要手動釋放內(nèi)存,但是 Java 不需要。

這主要得益于 Java 的虛擬機垃圾回收機制,它可以幫助開發(fā)者自動回收內(nèi)存中的對象,不需要手動釋放內(nèi)存,但是有些東西它是無法回收的,例如端口、顯存、文件等,超出了虛擬機能夠釋放資源的界限。

如果對未關(guān)閉流的文件進行讀寫操作,可能就會報錯,告訴你這個文件被某個進程占用。如果不手動釋放資源,隨著資源占有量逐漸增多,垃圾會越來越多,最終可能導(dǎo)致系統(tǒng)無法存儲其他的資源,甚至?xí)霈F(xiàn)系統(tǒng)崩潰。

一般來說,只要存在 IO 流讀寫操作,無論使用到的是網(wǎng)絡(luò) IO 或者文件 IO,都是需要和計算機內(nèi)的資源打交道的,清理計算機上面的垃圾,Java 的虛擬機垃圾回收機制沒有這個能力。

熟悉 Java 虛擬機垃圾回收機制的同學(xué),可能知道 gc 有兩個顯著的特點:

gc 只能釋放內(nèi)存資源,而不能釋放與內(nèi)存無關(guān)的資源gc 回收具有不確定性,也就是說你根本不知道它什么時候會回收

所以進行流的操作時,凡是跨出虛擬機邊界的資源都要求程序員自己手動關(guān)閉資源。

可能有的同學(xué)又發(fā)出疑問,我平時本地測試的時候沒有發(fā)現(xiàn)這個問題,為什么部署到線上就出這個提示的呢?

以讀取文件的FileInputStream流為例,其實里面隱含了一個finalize方法,當虛擬機進行垃圾回收之前,會調(diào)用這個方法。

打開源碼,你會發(fā)現(xiàn)底層調(diào)用的其實是close釋放資源的方法,可以看到 JDK 間接的幫助開發(fā)者進行最后一次的兜底。

/** * Ensures that the close method of this file input stream is * called when there are no more references to it. * * @exception  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();    }}

這就解釋了,為什么只是時不時的會出現(xiàn)提示,并不是總是。這個方法什么時候被調(diào)用,這取決于虛擬機的垃圾回收頻次。

但是在實際的開發(fā)過程中,開發(fā)者不能完全依賴虛擬機幫你回收這些系統(tǒng)資源,只要涉及到流的操作,強烈建議大家一定要手動關(guān)閉釋放資源,避免出現(xiàn)一些不必要的bug。

具體如何手動釋放資源資源呢,我們接著看!

三、正確的關(guān)閉流姿勢介紹

我們深知在操作 Java 流對象后要將流進行關(guān)閉,但是現(xiàn)實的情況卻往往不盡人意,原因是每個開發(fā)者的寫法可能不盡相同,不同的寫法導(dǎo)致出現(xiàn)各種千奇百怪的問題,下面我們一起來看看幾種關(guān)閉流的代碼案例!

寫法 1:在 try 中關(guān)流,而沒在 finally 中關(guān)流
try {    OutputStream out = new FileOutputStream("file");    // ...操作流代碼    out.close();} catch (Exception e) {    e.printStackTrace();}

當操作流代碼報錯的時候,這種寫法會導(dǎo)致流無法正常的關(guān)閉,因此不推薦采用!

正確的操作方式,應(yīng)該在finally里面完成,實例代碼如下:

OutputStream out = null;try {    out = new FileOutputStream("file");    // ...操作流代碼} catch (Exception e) {    e.printStackTrace();} finally {    // 在 finally 中進行關(guān)閉,確保一定能被執(zhí)行    try {        if (out != null) {            out.close();        }    } catch (Exception e) {        e.printStackTrace();    }}
寫法 2:在關(guān)閉多個流時,將其放在一個 try 中

在關(guān)閉多個流時,有的同學(xué)嫌棄麻煩,將其放在一個 try 中完成,實例代碼如下:

OutputStream out1 = null;OutputStream out2 = null;try {    out1 = new FileOutputStream("file");    out2 = new FileOutputStream("file");    // ...操作流代碼} catch (Exception e) {    e.printStackTrace();} finally {    try {        if (out1 != null) {            // 如果此處出現(xiàn)異常,則out2流沒有被關(guān)閉            out1.close();        }        if (out2 != null) {            out2.close();        }    } catch (Exception e) {        e.printStackTrace();    }}

這種寫法下,當out1.close出異常的時候,out2.close是不會被正常關(guān)閉的,因此不推薦采用!

正確的操作方式,應(yīng)該是一個一個的close,別偷懶,實例代碼如下:

OutputStream out1 = null;OutputStream out2 = null;try {    out1 = new FileOutputStream("file");    out2 = new FileOutputStream("file");    // ...操作流代碼} catch (Exception e) {    e.printStackTrace();} finally {    try {        if (out1 != null) {            out1.close();        }    } catch (Exception e) {        e.printStackTrace();    }    try {        if (out2 != null) {            out2.close();        }    } catch (Exception e) {        e.printStackTrace();    }}
寫法 3:在循環(huán)中創(chuàng)建流,在循環(huán)外關(guān)閉

有的同學(xué)在循環(huán)操作多個文件時,在循環(huán)外關(guān)閉文件流,實例代碼如下:

OutputStream out = null;try {    for (int i = 0; i < 10; i++) {        out = new FileOutputStream("file");        // ...操作流代碼    }} catch (Exception e) {    e.printStackTrace();} finally {    try {        if (out != null) {            out.close();        }    } catch (Exception e) {        e.printStackTrace();    }}

表面看上去好像沒有問題,但是實際上創(chuàng)建了 10 個 IO 流,try 里面的邏輯執(zhí)行完成之后,只是把最后的一個 IO 流對象賦予給了out參數(shù)。也就是當程序執(zhí)行完畢之后,只關(guān)閉了最后一個 IO 流,其它 9 個 IO 流沒用被手動關(guān)閉,因此不推薦采用!

正確的操作方式,應(yīng)該是在循環(huán)體內(nèi)close,別偷懶,實例代碼如下:

for (int i = 0; i < 10; i++) {    OutputStream out = null;    try {        out = new FileOutputStream("file");        // ...操作流代碼    } catch (Exception e) {        e.printStackTrace();    } finally {        try {            if (out != null) {                out.close();            }        } catch (Exception e) {            e.printStackTrace();        }    }}
寫法 4:關(guān)閉多個流時,沒用遵循后定義先釋放原則

有的同學(xué)在操作多個文件流時,操作完成之后,依照先后次序進行關(guān)閉文件流,實例代碼如下:

FileOutputStream fos = null;BufferedOutputStream bos = null;try {    fos = new FileOutputStream("file");    bos = new BufferedOutputStream(fos);    // ...操作流代碼} catch (Exception e){} finally {    // 依次關(guān)閉流    try {        fos.close();    } catch (IOException e) {        e.printStackTrace();    }    try {        // 此處會報 java.io.IOException: Stream Closed 錯誤        bos.close();    } catch (IOException e) {        e.printStackTrace();    }}

按照先后順序關(guān)閉文件流,這種寫法下,有可能會報java.io.IOException: Stream Closed錯誤。

原因是BufferedOutputStream依賴于FileOutputStream,如果直接關(guān)閉FileOutputStream流,再次關(guān)閉BufferedOutputStream,會提示源頭已經(jīng)被關(guān)閉,緩存區(qū)數(shù)據(jù)無法輸出。

正確的操作方式,應(yīng)該遵循后定義先釋放的原則,實例代碼如下:

FileOutputStream fos = null;BufferedOutputStream bos = null;try {    fos = new FileOutputStream("file");    bos = new BufferedOutputStream(fos);    // ...操作流代碼} catch (Exception e){} finally {    // 后定義先釋放    try {        bos.close();    } catch (IOException e) {        e.printStackTrace();    }    try {        fos.close();    } catch (IOException e) {        e.printStackTrace();    }}
寫法 5:jdk7 及以上版本,推薦采用 try-with-resources 寫法

try-with-resources是 JDK 7 中引入的一個新的異常處理機制,它能讓開發(fā)人員不用顯式的釋放try-catch語句塊中使用的資源。

以上文為例,可以改成如下寫法:

try (FileOutputStream fos = new FileOutputStream("file");     BufferedOutputStream bos = new BufferedOutputStream(fos)){    // ...操作流代碼} catch (Exception e){    e.printStackTrace();}

try-with-resources釋放資源的操作,也是遵循的后定義先釋放的原則!

寫法 6:使用包裝流時,只需要關(guān)閉最后面的包裝流即可

包裝流是指通過裝飾設(shè)計模式實現(xiàn)的 IO 流類,其目的是對底層流的功能進行擴展,在實際數(shù)據(jù)傳輸?shù)臅r候,還是使用底層流進行傳輸。比如緩存字節(jié)輸出流BufferedOutputStream就是一個包裝流,目的是對字節(jié)輸出流提供一個緩存區(qū)功能,讓數(shù)據(jù)輸出效率更高。

在使用到包裝流的時候,我們只需要關(guān)閉最后面的包裝流即可。

以上文為例,改寫的實例代碼如下:

InputStream is = null;InputStreamReader isr = null;BufferedReader br = null;try {    is = new FileInputStream("file");    isr = new InputStreamReader(is);    br = new BufferedReader(isr);    // ...操作流代碼} catch (Exception e){    e.printStackTrace();} finally {    // 關(guān)閉包裝流,也會自動關(guān)閉 InputStream 流    try {        br.close();    } catch (IOException e) {        e.printStackTrace();    }}

這是因為,包裝流關(guān)閉時會調(diào)用原生流的關(guān)閉方法,請看源碼!

public void close() throws IOException {    synchronized (lock) {        if (in == null)            return;        try {            // 這里的in 指的是 InputStreamReader,最后會原生流的close方法            in.close();        } finally {            in = null;            cb = null;        }    }}
四、內(nèi)存流是否需要關(guān)閉?

在上文中,我們提到只要是 IO 流都建議大家手機關(guān)閉資源,但是在 Java 中有一種流,它是不需要手動關(guān)閉的,比如內(nèi)存讀寫流:ByteArrayInputStream、ByteArrayOutputStream。

不同于指向硬盤的流,ByteArrayInputStream和ByteArrayOutputStream其實是偽裝成流的字節(jié)數(shù)組存儲在內(nèi)存中(把它們當成字節(jié)數(shù)據(jù)來看就好了),他們不會鎖定任何文件句柄和端口,如果不再被使用,字節(jié)數(shù)組會被垃圾回收掉,所以不需要關(guān)閉。

當 IO 流是指向存儲卡 / 硬盤 / 網(wǎng)絡(luò)等外部資源的流,是一定要手動關(guān)閉的。

五、小結(jié)

本位主要圍繞【為什么 IO 流必須手動關(guān)閉,不能像其他的方法坐等 GC 處理】這個話題進行一次內(nèi)容的整合和總結(jié),同時也給出了推薦的正確關(guān)閉 IO 流的寫法。

在實際的開發(fā)過程中,建議大家正確的使用 IO 流,以免出現(xiàn)各種 bug !

內(nèi)容難免有所遺漏,歡迎網(wǎng)友留言指出。

六、參考

1、csdn - 演員12138 - IO流為什么必須手動關(guān)閉,不能像其他的方法坐等GC處理

2、csdn - 思想永無止境 - Java之關(guān)閉流

關(guān)鍵詞:

上一篇:泉州市印發(fā)《關(guān)于加強孤獨癥兒童關(guān)愛服務(wù)的若干措施》
下一篇:最后一頁

亚洲少妇一区二区三区_精品成人无码一区二区三区_日韩影院一区二区_a天堂视频在线观看_草草影院第一页_香蕉视频污视频_99久久国产精_91成人在线观看喷潮蘑菇_天堂www中文在线资源_精品无码人妻一区二区免费蜜桃_国产黑丝一区二区
喷水视频在线观看| 在线观看黄网址| 我要看一级黄色录像| 在线观看国产免费视频| 99久久人妻精品免费二区| 99国产精品免费视频| avtt中文字幕| 欧美性生交xxxxx| 五月天激情小说| 中字幕一区二区三区乱码| 超薄肉色丝袜一二三| eeuss中文字幕| 日韩一级片大全| 97精品人妻一区二区三区蜜桃| 日本人添下边视频免费| 久久国产精品无码一级毛片| 自拍偷拍视频亚洲| 亚洲一二三在线观看| 女人扒开腿免费视频app| 污污免费在线观看| 日本一区二区视频在线播放| 久久久久亚洲AV成人无在| 九九精品视频免费| 怡红院一区二区| 五月天免费网站| 最新国产精品自拍| 国产激情av在线| 69亚洲乱人伦| 亚洲波多野结衣| 久久精品国产亚洲AV熟女| 黄色片子在线观看| 特级西西人体wwwww| 在线免费观看亚洲视频| 久久久久久国产精品无码| 精品国产欧美日韩不卡在线观看 | 特级西西www444人体聚色| 午夜国产福利一区二区| 插吧插吧综合网| 911亚洲精选| 日韩三级在线观看视频| 国产av自拍一区| 这里只有精品在线观看视频| 天天天天天天天天操| 色哟哟精品观看| www.啪啪.com| 麻豆av免费看| 放荡的美妇在线播放| 亚洲色图100p| 成人免费毛片糖心| 泷泽萝拉在线播放| 添女人荫蒂视频| 香蕉视频污视频| a级一a一级在线观看| 秋霞午夜鲁丝一区二区| 特一级黄色录像| 污软件在线观看| 日本一二三区在线观看| 韩国一级黄色录像| 国产激情无码一区二区三区| 免费成人深夜蜜桃视频| 91制片厂在线| 特级片在线观看| 一级黄色免费毛片| 韩国黄色一级片| 亚洲调教欧美在线| 久久精品国产亚洲AV熟女| 91精品人妻一区二区| www.中文字幕av| 毛片aaaaaa| 国产精品 欧美激情| 午夜性福利视频| 五月婷婷综合在线观看| 中文字幕免费高清| 91制片厂在线| 一级黄色大片免费看| 永久免费看mv网站入口78| 91社区视频在线观看| 久久无码人妻一区二区三区| 日本人添下边视频免费| 舐め犯し波多野结衣在线观看| 久久久久久成人网| 日本人dh亚洲人ⅹxx| 精品人妻少妇嫩草av无码| 成人性视频免费看| 91视频免费入口| 国产综合精品在线| 中文字幕avav| 婷婷色一区二区三区| 9191在线视频| 国产免费一区二区三区网站免费| 乱h高h女3p含苞待放| 巨胸大乳www视频免费观看| 性生交大片免费全黄| 成年人网站免费看| 欧美黑人猛猛猛| 日韩中文字幕有码| 国产激情第一页| 91视频免费在线看| 亚洲a∨无码无在线观看| 中文视频在线观看| 成人观看免费视频| 久久99久久99精品免费看小说| 欧美深性狂猛ⅹxxx深喉 | 欧美黑人欧美精品刺激| 国产麻豆视频在线观看| 国产中年熟女高潮大集合| 国产麻豆剧传媒精品国产| 日本少妇aaa| 精品无人区无码乱码毛片国产| aaaa黄色片| 国产成人av无码精品| 日本少妇一级片| 岛国毛片在线观看| 麻豆精品国产免费| 波兰性xxxxx极品hd| 少妇太紧太爽又黄又硬又爽小说| 懂色av粉嫩av蜜乳av| 视频免费在线观看| 日本美女视频网站| 国产xxx在线观看| 俄罗斯黄色录像| 亚洲精品久久一区二区三区777 | 97成人资源站| 午夜写真片福利电影网| 杨幂一区二区国产精品| 天天看片中文字幕| 国产51自产区| a级一a一级在线观看| 欧美亚一区二区三区| 人妻精品久久久久中文| 国精产品一区一区| 污软件在线观看| 精品人妻无码中文字幕18禁| 国产精品成人99一区无码| 国产人妻精品午夜福利免费| v天堂中文在线| 成人无码av片在线观看| 五月天婷婷丁香网| 国产黄色一区二区三区| 三上悠亚 电影| 中文成人无字幕乱码精品区| av中文字幕免费观看| 青青青视频在线免费观看| 老湿机69福利| 国产精品无码久久久久一区二区| 久久久久久久毛片| 18岁成人毛片| 无码人妻aⅴ一区二区三区69岛| 老司机成人免费视频| 人妖粗暴刺激videos呻吟| 国产精品成人在线视频| 亚洲欧美日韩色| 久久免费手机视频| 欧美xxxxx精品| 日韩一区二区三区四区在线| 黄色正能量网站| 中文字幕在线有码| 成人小视频免费看| 欧美图片自拍偷拍| 亚洲AV成人无码网站天堂久久| 天天躁日日躁狠狠躁免费麻豆| 国产传媒在线看| 久久久久国产精品区片区无码| 午夜剧场免费在线观看| 中文字幕免费在线看线人动作大片| 精品无码久久久久成人漫画| 嘿嘿视频在线观看| av女人的天堂| 亚洲一区二区在线免费| 青青青在线免费观看| 欧美美女性生活视频| 欧美特级黄色录像| 少妇户外露出[11p]| 97中文字幕在线观看| 波多野结衣在线网址| 亚洲女人久久久| www.黄色在线| 欧美午夜激情影院| 永久免费看mv网站入口78| 成人免费看aa片| 国产人妻一区二区| 国产麻豆天美果冻无码视频| 国产精品一区二区无码对白| 任你躁av一区二区三区| 久草免费资源站| 久久性爱视频网站| 插吧插吧综合网| 少妇人妻好深好紧精品无码| 亚洲女优在线观看| 国产精品国产三级国产传播| 99自拍视频在线| 香蕉在线观看视频| zjzjzjzjzj亚洲女人| 337p日本欧洲亚洲大胆张筱雨| 国产吃瓜黑料一区二区| 国产精品无码专区| 国产精品密蕾丝袜| 一区视频免费观看| 最近日本中文字幕| 黄色av片三级三级三级免费看|