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

當(dāng)前位置:首頁(yè) > 今日熱點(diǎn) > 民生資訊 > 正文

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

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

在項(xiàng)目的開(kāi)發(fā)過(guò)程中,當(dāng)我們對(duì)文件進(jìn)行讀寫(xiě)操作時(shí),不知道大家有沒(méi)有碰到這樣的問(wèn)題。

有的同學(xué)在做一個(gè)讀取臨時(shí)文件數(shù)據(jù)的工作,當(dāng)讀完文件內(nèi)容,準(zhǔn)備將其刪除的時(shí)候,有時(shí)候會(huì)正常,但有時(shí)候會(huì)提示:操作無(wú)法完成,因?yàn)槲募言?Java? Platform SE binary 中打開(kāi),編譯器也會(huì)提示:Resource leak: "xxxx" is never closed。


(資料圖)

樣例代碼如下:

File file = new File("xxx.txt");// 實(shí)例化輸入流FileReader reader = new FileReader(file);// 緩沖區(qū)char[] buffer = new char[1024];// 分次讀取數(shù)據(jù),每次最多讀取1024個(gè)字符,將數(shù)據(jù)讀取到緩沖區(qū)之中,同時(shí)返回讀取的字節(jié)個(gè)數(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)過(guò)排查,發(fā)現(xiàn)出現(xiàn)該問(wèn)題的原因是:讀取文件的 IO 流沒(méi)有正常的關(guān)閉,導(dǎo)致文件一直被流持有,刪除文件不成功!

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

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

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

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

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

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

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

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

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

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

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

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

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

以讀取文件的FileInputStream流為例,其實(shí)里面隱含了一個(gè)finalize方法,當(dāng)虛擬機(jī)進(jìn)行垃圾回收之前,會(huì)調(diào)用這個(gè)方法。

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

/** * 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();    }}

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

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

具體如何手動(dòng)釋放資源資源呢,我們接著看!

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

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

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

當(dāng)操作流代碼報(bào)錯(cuò)的時(shí)候,這種寫(xiě)法會(huì)導(dǎo)致流無(wú)法正常的關(guān)閉,因此不推薦采用!

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

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

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

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流沒(méi)有被關(guān)閉            out1.close();        }        if (out2 != null) {            out2.close();        }    } catch (Exception e) {        e.printStackTrace();    }}

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

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

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();    }}
寫(xiě)法 3:在循環(huán)中創(chuàng)建流,在循環(huán)外關(guān)閉

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

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();    }}

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

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

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();        }    }}
寫(xiě)法 4:關(guān)閉多個(gè)流時(shí),沒(méi)用遵循后定義先釋放原則

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

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 {        // 此處會(huì)報(bào) java.io.IOException: Stream Closed 錯(cuò)誤        bos.close();    } catch (IOException e) {        e.printStackTrace();    }}

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

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

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

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();    }}
寫(xiě)法 5:jdk7 及以上版本,推薦采用 try-with-resources 寫(xiě)法

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

以上文為例,可以改成如下寫(xiě)法:

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

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

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

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

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

以上文為例,改寫(xiě)的實(shí)例代碼如下:

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)閉包裝流,也會(huì)自動(dòng)關(guān)閉 InputStream 流    try {        br.close();    } catch (IOException e) {        e.printStackTrace();    }}

這是因?yàn)椋b流關(guān)閉時(shí)會(huì)調(diào)用原生流的關(guān)閉方法,請(qǐng)看源碼!

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

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

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

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

五、小結(jié)

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

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

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

六、參考

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

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

關(guān)鍵詞:

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

亚洲少妇一区二区三区_精品成人无码一区二区三区_日韩影院一区二区_a天堂视频在线观看_草草影院第一页_香蕉视频污视频_99久久国产精_91成人在线观看喷潮蘑菇_天堂www中文在线资源_精品无码人妻一区二区免费蜜桃_国产黑丝一区二区
亚洲精品天堂网| aaaa黄色片| 四虎地址8848| 无码少妇一区二区| 美国黄色一级毛片| 国产精品一区二区人妻喷水| 少妇影院在线观看| 久久久久亚洲av片无码| 香蕉久久久久久久| 日韩av片在线免费观看| 日本污视频网站| 91狠狠综合久久久| 青花影视在线观看免费高清| 91嫩草|国产丨精品入口| 1024手机在线视频| 亚洲天堂2024| 少妇大叫太粗太大爽一区二区| 三级黄色片网站| 国产成人免费观看网站| 日韩av手机在线免费观看| 性欧美一区二区| 91大神福利视频| 艳妇荡乳欲伦69影片| 9.1在线观看免费| 国产交换配乱淫视频免费| 丰满的亚洲女人毛茸茸| 麻豆精品一区二区三区视频| 97人妻精品一区二区三区免费| 人体私拍套图hdxxxx| 老熟妇一区二区| 国产高潮失禁喷水爽到抽搐| 强伦人妻一区二区三区| 免费三级在线观看| 亚洲一区二区三区黄色| 亚洲第一黄色网址| 久艹在线观看视频| 黄色a一级视频| 538任你躁在线精品视频网站| 一级黄色大片免费看| 美女黄色一级视频| 少妇视频在线播放| 性猛交╳xxx乱大交| 粉嫩精品久久99综合一区| 欧美熟妇精品一区二区蜜桃视频| 一级肉体全黄裸片| 日本黄色动态图| 日本女人黄色片| 国产18无套直看片| 成人免费无码大片a毛片| 黄色片子免费看| 中文字幕无码日韩专区免费| 成人精品在线观看视频| 欧洲成人午夜精品无码区久久| 免费成人深夜蜜桃视频 | 国产真实乱在线更新| 中国毛片在线观看| 一区二区视频观看| 亚洲の无码国产の无码步美| 伊人久久久久久久久| www.99re7| 国产探花一区二区三区| 国产稀缺精品盗摄盗拍| 男人操女人下面视频| 亚洲精品成人无码毛片| 中文字幕av久久爽av| 亚洲二区在线播放| 日本一区二区三区在线免费观看| 熟妇无码乱子成人精品| 少妇伦子伦精品无吗| 国产成人av片| 极品白嫩丰满美女无套| 精品久久久久一区二区| 日本50路肥熟bbw| 熟妇高潮一区二区| 精品一区二区三区四区五区六区| 无码成人精品区在线观看| 波多野结衣av在线免费观看| 人妻av无码一区二区三区| 我不卡一区二区| 国产精品视频一区二区在线观看| 呻吟揉丰满对白91乃国产区| 国产精品无码无卡无需播放器| 亚洲色图第四色| 69av.com| 久久丫精品国产亚洲av不卡| 欧美激情视频二区| 91亚洲一线产区二线产区| 91av在线免费| 黄色香蕉视频在线观看| 久久久久亚洲av无码专区首jn| av无码一区二区三区| 97人妻精品一区二区免费| 一二三四国产精品| 亚洲黄色小说在线观看| 白白色免费视频| 波多野结衣三级视频| 天天操天天舔天天射| 国产高潮流白浆| 国产xxxxxxxxx| 99热6这里只有精品| 91传媒理伦片在线观看| 99久久99久久精品免费看小说.| 成年人一级黄色片| 加勒比综合在线| 在线观看亚洲免费视频| 99久久久免费精品| 成年人av电影| 日日噜噜夜夜狠狠久久波多野| 蜜桃传媒一区二区亚洲| 99久久国产精| 丰满人妻一区二区三区大胸| 日韩一区二区a片免费观看| 性生交大片免费看l| 国产又粗又长又硬| 中文字幕国产综合| 双性尿奴穿贞c带憋尿| 精品国产免费久久久久久婷婷| 中文在线一区二区三区| 99热这里只有精品2| 国产 xxxx| 色综合久久久无码中文字幕波多| 国产午夜精品理论片在线| 麻豆传媒在线看| 神马久久久久久久久久久| 最新黄色av网址| 中文幕无线码中文字蜜桃| 成人片黄网站色大片免费毛片| 能直接看的av| 无码人妻少妇色欲av一区二区| 欧美亚一区二区三区| 久久久国产一级片| 黄色片子在线观看| 登山的目的在线| 亚洲综合视频网站| 特大黑人巨人吊xxxx| 国产精品熟妇一区二区三区四区 | 日韩av网站在线播放| 成人无码av片在线观看| 精品国产aaa| 潮喷失禁大喷水aⅴ无码| 欧美性生交大片| 青青草精品在线| 日本黄色免费观看| 无码人妻丰满熟妇啪啪欧美| 性少妇xx生活| 潘金莲一级淫片aaaaaaa| 韩国三级在线看| 麻豆国产精品一区| 亚洲一级片在线播放| 最新一区二区三区| 欧美做受喷浆在线观看| 国产又黄又粗又猛又爽的| 粗大的内捧猛烈进出视频| 日韩中文字幕电影| a级大片免费看| 久久久久久九九九九九| 欧洲猛交xxxx乱大交3| 熟女俱乐部一区二区| 男人女人拔萝卜视频| 国产免费无遮挡吸奶头视频| 在线观看欧美一区二区| 国产精品www爽爽爽| 日批在线观看视频| 欧洲猛交xxxx乱大交3| 亚洲第一综合网| free性中国hd国语露脸| 亚洲国产综合av| 97在线观看视频免费| av男人的天堂av| 少妇户外露出[11p]| 女女调教被c哭捆绑喷水百合| 东京热无码av男人的天堂| 日韩av手机在线播放| 国产大片免费看| 日日碰狠狠添天天爽| 国产真实乱人偷精品人妻| 国产午夜在线一区二区三区| 91在线播放观看| 久久免费看少妇高潮v片特黄| 大胸美女被爆操| 日本污视频网站| 天天干天天操天天拍| www.日本高清视频| 丁香激情五月少妇| 男人的天堂av网| 国产免费嫩草影院| 亚洲综合视频网站| 丰满少妇被猛烈进入一区二区| 又色又爽的视频| 三级黄色录像视频| 麻豆视频在线免费看| 永久看看免费大片| youjizz.com日本| 一级特级黄色片| 亚洲国产精品自拍视频| 国产高清自拍视频| 摸摸摸bbb毛毛毛片| 九九精品视频免费| 一级黄色免费视频| 日韩在线免费观看av|