2012-05-10 42 views
0

我正在嘗試讀取大文件(> 150MB)並將文件內容作爲ByteArrayOutputStream返回。這是我的代碼...讀取文件(> 150MB)並將文件內容作爲ByteArrayOutputStream返回

private ByteArrayOutputStream readfileContent(String url) throws IOException{ 

    log.info("Entering readfileContent "); 
    ByteArrayOutputStream writer=null; 
    FileInputStream reader=null; 

    try{ 
     reader = new FileInputStream(url); 
     writer = new ByteArrayOutputStream(); 

     byte[] buffer = new byte[1024]; 

     int bytesRead = reader.read(buffer); 
     while (bytesRead = > -1) { 
      writer.write(buffer, 0, bytesRead); 
      buffer = new byte[1024]; 
     } 

    } 
    finally { 
     writer.close(); 
    } 

    log.info("Exiting readfileContent "); 
    return writer; 
} 

我得到一個java.lang.OutOfMemoryError: Java heap space exception。我曾嘗試增加java堆大小,但它仍然會發生。有人可以幫助解決這個問題。

+1

不要這樣做。該文件太大,無法一次讀入內存。你爲什麼認爲你需要一個ByteArrayOutputStream?來電者將如何處理此流?爲什麼不只是返回一個FileInputStream並讓調用者讀取它呢? – Cheeso

+0

你可能想讀大塊內容的文件內容 – Rakesh

+0

備註:1. in!= null'是多餘的--'in'永遠不會變成'null'。 2.你對'長度'var做什麼是一種不正當的行爲。 –

回答

0

您的方法將至少使用與文件相同的內存量,但由於ByteArrayOutputStream使用字節數組作爲存儲,它可能必須調整自身大小150,000次(150兆/ 1024k緩衝區),其中效率不高。將堆大小增加到2 *的文件大小,並將buf的大小增加到更大的大小可能會允許它運行,但正如其他海報所說,隨着時間的推移讀取文件比讀取文件好得多,而不是讀取它作爲一個字符串。

+1

確切的最壞情況要求是3 *大小 - 有一個時刻,舊陣列和新陣列必須共存 - 並且2 *大小必須作爲連續的堆可用。所以即使有很多可用內存,這也可能會失敗。 –

+0

否 - 不正確。最壞的情況比你想象的要糟糕。我不知道java內存分配器,但是當你分配一個緩衝區時,我想它不會簡單地分配足夠的內存來「恰好」大小合適。通常分配器返回的是2的冪或者其他矮塊大小的塊。所以最壞的情況可能是5X或更多。你不能確定。無論如何,你的結論是正確的 - 無論你對堆積做什麼,它仍然會受到OOME的影響。流媒體是解決方案。 – Cheeso

1

您應該返回BufferedInputStream並讓調用者從中讀取它。你正在做的是將整個文件作爲ByteArrayOutputStream複製到內存中。

你的問題是缺少你想要做的文件內容。沒有這個,我們只能猜測。有一個ServletOutputStream註釋掉。你最初想寫這個嗎?寫入此代替ByteArrayOutputStream應該工作。

+0

文件內容爲ByteArrayOutputStream是FAST ESP的輸入 –

0

由於您知道要讀取的字節數,因此您可以通過創建尺寸爲ByteArrayOutputStream的文件來節省時間和空間。這將節省「增長」後備存儲的時間和空間開銷。 (我沒有看過代碼,但它可能使用與StringBuilder相同的策略;即每次分配時將分配翻一倍。該策略最終可能會在高峯使用時最多使用3倍的文件大小。)

(坦白地說,把輸出到ByteArrayOutputStream當你知道的大小似乎有點毫無意義,只是分配的字節數組足夠大,直接讀成。)

除此之外,答案是,你需要使堆更大。

+0

文件大小不是恆定的,它的動態,因爲它在讀下一個文件時會改變。我試圖增加流的大小到文件大小(file.length)的大小,但仍然是同樣的問題。 –

+0

如果這是一個文件,你可以使用'file.length()'獲得文件大小。事實上,你的代碼的原始版本就是這樣做的。如果您仍然遇到OOM異常,那麼您只需增加堆大小,直到堆足夠大以容納整個文件。 (或者,更改程序的其餘部分,以便不需要將該文件保存在內存中......) –

1

while循環中有錯誤。將其更改爲

while (bytesRead >= -1) { 
    writer.write(buffer, 0, bytesRead); 
    bytesRead = reader.read(buffer); 
} 

另外,請不要忘記關閉reader

(它仍然需要相當大的存儲量。)

+0

我已經做到了這一點,但仍然是同樣的問題。 –

+0

這不是一個'也'/替代方案,您的原始代碼是一個無限循環,它會寫入前1024個字節輸出,直到jvm崩潰。對於調試,您可能只想將整個文檔讀入一個字節數組,然後通過(肯定快速api支持在輸出流被接受的任何地方使用數組)。這將允許您將閱讀文檔時的問題縮小爲字節數組,而不是通過快速API可能對流進行的任何操作。 – sbaker

0

我已經不具有主機上沒有足夠的連續虛擬內存出現在C#中的Windows事業類似的問題。如果你在Windows上,你可以嘗試增加虛擬機空間。

相關問題