2017-02-04 61 views
0

最近,我創建了強制用戶界面實現單fromStream(OutputStream)使用它的默認方法看起來像這樣:什麼讓文件讀取沒有緩衝區如此昂貴?

public default T fromFile(File file) throws IOException { 
    try (InputStream stream = new FileInputStream(file)) { 
     return fromStream(stream); 
    } 
} 

後不久事實證明,這是非常昂貴(每MB幾秒鐘)由於單字節被直接從FileInputStream讀取。

將其包裝在BufferedInputStream解決了我的問題,但它給我留下了爲什麼FileInputStream是如此非常昂貴的問題。

文件頻道未關閉或讀取字節時打開,所以爲什麼有需要擺在首位的緩衝區?

回答

3

如果您使用read()方法從非緩衝流中讀取字節,JVM將最終爲操作系統重複讀取系統調用以從文件讀取單個字節。 (在引擎蓋下,JVM可能調用read(addr, offset, count),計數爲1.)

使系統調用的開銷很大。至少比常規方法調用多幾個數量級。這是因爲有顯著開銷:

  • 申請(授權)的安全域和系統(特權)安全域之間的切換上下文。需要保存寄存器組,需要更改虛擬內存映射,需要刷新TLB條目等。
  • 操作系統必須做各種額外的事情來確保系統調用請求是合法的。在這種情況下,操作系統必須根據當前文件的位置和大小,地址是否在應用程序的地址空間內以及映射爲可寫入來確定請求的偏移量和計數是否正常。等等。

相比之下,如果您使用緩衝流,則流會嘗試以大塊讀取操作系統中的文件。這通常會導致系統調用數量減少數千倍。


實際上,這不是關於如何將文件存儲在磁盤上。確實,數據最終必須一次一個塊地讀取,等等。但是,操作系統足夠聰明,可以進行自己的緩衝。它甚至可以預讀文件的部分,以便他們在(內核)內存準備好應用時,它使系統調用來讀。

多個一字節read()調用極其不可能導致額外的磁盤流量。唯一可行的方案是,如果您在每個read()之間等待很長時間...操作系統會重用緩存磁盤塊的空間。

1

當你從文件中讀取,你必須在一個時間來閱讀它的模塊,因爲這是硬件支持只的數量。如果你讀一次一個字符沒有緩衝,則假設512B塊,你會讀出同一塊512次讀取整個塊。如果您讀取和緩衝,您將訪問磁盤一次,然後從內存中讀取。

訪問磁盤的大小比訪問內存慢幾個數量級,鄰這不是一個好主意。

+0

那麼,一個非常差的操作系統可以這樣做,但每個具有Java實現的操作系統都具有內部OS文件系統緩存,用於緩衝磁盤中的數據塊,因此該塊將不會再被讀取。但即使從文件系統緩存中讀取,也需要系統調用,與處於用戶進程內部的任何內容相比,這會帶來很大的開銷。見Stephen的回答。 –