2010-10-13 80 views
1

我知道,如果我這樣做InputStreams是GCed

copyFromInToOut(new FileInputStream(f1), new FileOutputStream(f2)); 
System.gc(); 

它將在那些FileInputStream的磨合的GC,關閉它們。但是,如果我做

copyFromInToOut(new BufferedInputStream(new FileInputStream(f1)), new BufferedOutputStream(new FileOutputStream(f2)); 
System.gc(); 

是否有任何危險,即FileOutputStream中會GCed在之前的BufferedOutputStream,不會造成緩衝區刷新? 我無法調用flush,close,因爲這需要更多的步驟。它首先涉及聲明一個緩衝輸入流,傳遞,然後調用close。或者我可以安全地做到這一點?

+2

你爲什麼試圖做垃圾收集器的工作? – 2010-10-13 21:55:53

+2

你爲什麼不願意採取「比這更多的步驟」? – erickson 2010-10-13 22:14:18

+0

您是否有實際證據證明垃圾收集器是A)在您的流正在使用時收集流?和B)關閉你的流?爲什麼你的流甚至有資格使用GC - 線程是否在執行處理程序,然後可能是流程終結器方法被調用。 – Justin 2010-10-13 22:18:13

回答

8

不要叫System.gc()明確。不要依賴終結者來做任何事情。特別是如果你不明白垃圾收集是如何工作的。顯式垃圾收集請求可以被忽略,並且終結器可能永遠不會運行。

一個精心編寫的copyFromInToOut方法可能會在內部使用自己的緩衝區,所以封裝輸出應該是不必要的。

聲明爲FileInputStreamFileOutputStream變量,並在finally塊調用每個close()

FileInputStream is = new FileInputStream(f1); 
try { 
    FileOutputStream os = new FileOutputStream(f2); 
    try { 
    copyFromInToOut(is, os); 
    os.flush(); 
    } finally { 
    os.close(); 
    } 
} finally { 
    is.close(); 
} 
+0

一個小的評論。如果close()在try塊已經存在的時候決定拋出一個Exception,那麼在finally塊中關閉close()不會引起IOException異常。 (或者更一般地說,決不允許在代碼中拋出異常{}) – 2010-10-14 07:48:43

+0

我不會說它會「導致問題」。會發生什麼情況是塊的結果從'try'塊中拋出的異常變爲'close()'拋出的異常。有時候,這是不可取的。在很多情況下,你只是不在乎。 Java 7的ARM功能可以使它以一致的方式輕鬆處理。 – erickson 2010-10-14 17:42:52

5

沒有InputStream的實現,我知道它將爲您在GCd時爲 close()。您必須手動輸入流量 close()

編輯:顯然FileInputStream無法接近()爲您的finalize方法,我是不知道的,但看到其他的答案爲什麼你不應該依賴於這種情況的原因。

在上面的兩個示例中,您必須關閉輸入流和輸出流。對於包裹緩衝的情況下,對於任何包裝的情況下,你應該只需要調用close()在最外層的InputStream,在這種情況下BufferedInputStream

+1

FileInputStream將在它的'finalized()'方法中關閉自己 – irreputable 2010-10-13 22:02:49

+0

雖然你確實應該自己關閉()它們(即使你調用System.gc()),當對象被gc化時你也無法控制)是錯的。例如OpenJDK在它的FileInputStream的finalize()中做了.close()。 – nos 2010-10-13 22:26:12

+0

第一段不正確。 FileInputStream(例如)*將*關閉垃圾收集的流。但依靠這個是一個非常糟糕的主意;看到我的答案。 – 2010-10-14 03:16:30

2

那麼這是有趣的分析什麼是真正發生的事情,當你做到這一點,只是不」這樣做。

總是明確關閉您的流。

(回答你的問題,是的,在字節的緩衝區可能沒有被刷新到文件I/GC時出現,他們是丟失O流)

2

流應明確使用@埃裏克森的回答中所示的模式被關閉。依託定稿關閉流對你來說是非常糟糕的主意

  1. 調用System.gc()是昂貴的,特別是因爲(如果確實有的話),以很可能引發一個完整的垃圾收集。這會導致中的每個可訪問對象中的每個引用都被跟蹤。

  2. 如果您閱讀了關於System.gc()的javadoc,您將會看到它僅僅是JVM運行GC的「提示」。一個JVM可以自由地忽略這個提示......它將我們引向下一個問題。

  3. 如果不明確運行GC,則可能需要很長時間才能運行GC。即使如此,也不能保證終結器立即運行。

在平均時間:

  • 所有打開的文件保持開放,利用他們在流
  • 任何不成文的數據有可能妨礙其他應用程序仍然不成文
  • Java應用程序甚至可以運行成爲打開其他流時遇到的問題將耗盡文件描述符插槽。

還有最後一個問題依靠最終化處理輸出流。如果流中的數據未完成,則輸出流類將嘗試刷新它。但是,這一切都發生在JVM內部線程上,而不是您應用程序的線程之一。因此,如果刷新失敗(例如,因爲文件系統已滿),則您的應用程序將無法捕獲由此產生的異常,因此將無法報告它或做任何事情來恢復。

編輯

回到原來的問題,事實證明了的BufferedOutputStream類沒有覆蓋默認Object.finalize()方法。這意味着BufferedOutputStrean在收集垃圾時不會被刷新。緩衝區中的任何未寫入的數據都將丟失。

這是明確關閉流的另一個原因。事實上,在這種特殊情況下,撥打System.gc()不僅是不好的做法,它也是可能導致數據丟失。