2012-07-12 25 views
2
發送大文件在插座

我的工作得到了服務器和客戶端應用程序,他們的工作完美而發送的小文件,但是當我嘗試發送例如電影文件是700MB超過插座它給了我也不會在Java

Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space 

我搜索了互聯網,發現了一些關於發送大文件的教程,但不能理解它們,但我認爲我的porblem是在寫文件。

這是服務器使用寫我的文件代碼:

output = new FileOutputStream(directory + "/" + fileName); 
      long size = clientData.readLong(); 
      byte[] buffer = new byte[1024]; 

      while (size > 0 && (bytesRead = clientData.read(buffer, 0, (int) Math.min(buffer.length, size))) != -1) { 
       output.write(buffer, 0, bytesRead); 
       size -= bytesRead; 
      } 
      output.close(); 

這裏是我的客戶用來發送文件的代碼:

byte[] fileLength = new byte[(int) file.length()]; 

     FileInputStream fis = new FileInputStream(file); 
     BufferedInputStream bis = new BufferedInputStream(fis); 

     DataInputStream dis = new DataInputStream(bis);  
     dis.readFully(fileLength, 0, fileLength.length); 

     OutputStream os = socket.getOutputStream(); 

     //Sending size of file. 
     DataOutputStream dos = new DataOutputStream(os); 
     dos.writeLong(fileLength.length); 
     dos.write(fileLength, 0, fileLength.length);  
     dos.flush(); 

     socket.close(); 
+0

你可以做的最好的事情就是像這樣運行你的程序http://stackoverflow.com/questions/542979/using-heapdumponoutofmemoryerror-parameter-for-heap-dump-for-jboss然後你可以使用jvisualvm來分析它例如。 – biziclop 2012-07-12 10:01:40

+0

我認爲你的問題在於你試圖找到X MB的內容到Y MB內存中,其中X> Y.如果這是真的,那你怎麼寫這個文件並不重要。這是例外情況告訴你的。 – duffymo 2012-07-12 10:02:55

+0

你想吃一口西瓜,所以你死了。嘗試讀取一小段文件並將其發送出去並重復該操作。 – 2012-07-12 10:21:36

回答

7

它給你OutOfMemoryError,因爲你正在嘗試在發送之前將整個文件讀入內存。這是100%完全和完全不必要的。只需讀取和寫入數據塊,就像在接收代碼中一樣。

+0

你可以編輯我的代碼嗎?因爲readFully()是唯一的方法,我知道如何去做。謝謝 – 2012-07-12 10:09:22

+0

我們可以做得更好,轉到http://docs.oracle.com/javase/tutorial/essential/io/ – MadProgrammer 2012-07-12 10:13:41

+0

@RohitMalish它*不是*'唯一的方法[你]知道如何去做'。看看你自己的接收代碼。那是怎麼做到的。你已經寫過。只要擺脫'readLong()'部分和從它流出的所有內容:讀取直到EOF。 – EJP 2012-07-12 10:16:57

4

問題是,您正試圖一次將整個文件加載到內存中(通過readFully),這超過了您的堆空間(默認情況下爲256mb,我認爲)。

你有兩個選擇:

  1. 好選項:加載塊(在時間,例如1024個字節)的文件,並將其發送這樣的。花費更多的努力來實現。
  2. 不良選項:通過-Xmx選項增加堆空間。不建議,我主要提到這一點,以防萬一您需要爲程序增加更多堆空間,但在這種情況下,這是一個非常糟糕的主意。

對於選項一個你可以嘗試這樣的:

DataInputStream in = new DataInputStream(new FileInputStream("file.txt")); 
byte[] arr = new byte[1024]; 
try { 
    int len = 0; 
    while((len = in.read(arr)) != -1) 
    { 
     // send the first len bytes of arr over socket. 
    } 
} catch(IOException ex) { 
    ex.printStackTrace(); 
} 
+0

我不是真的進入java,但對於一個700MB的文件,1024不是太小?它會創建很多電話。 – 2012-07-12 10:06:55

+0

@Karoly Horvath:這只是一個例子。我使用了1024,因爲這是他在接收端使用的值。 – Tudor 2012-07-12 10:07:28

+0

你能告訴我1.方法的例子嗎? – 2012-07-12 10:34:21

1

的問題是,在客戶端創建整個文件的字節數組。

byte[] fileLength = new byte[(int) file.length()]; //potentially huge buffer allocated here 

你應該在服務器端做同樣的事情,並按塊讀取文件塊到固定大小的緩衝區中。

0

就像您在服務器端使用緩衝區發送數據一樣,請使用applet /客戶端中的緩衝區來讀取它。如果您傳輸的是大文件,當您使用readFully()時,您將明顯用完內存。這種方法只適用於你知道你的數據將是真的,真的小的情況。

2

OutOfMemoryError當程序達到堆大小限制時拋出。你把整個文件加載到你的RAM中。默認堆大小是物理內存大小的1/4或1GB,因此您的程序達到了極限(您可能擁有2GB RAM,因此堆大小爲512MB)。

您應該以塊(例如10MB)讀取文件,這樣您將不會達到堆大小限制,並且您可以在發生某種錯誤時重新發送塊,而不是重新發送整個文件。你甚至可以在不同線程中讀取塊,所以當你發送一個塊時,你可以加載第二塊,並且當你發送第一塊時,你可以立即開始發送第二塊。

+0

其實我有4GB內存 – 2012-07-12 10:11:34

+0

所以你的堆大小是1GB。您可以達到極限,因爲您不僅爲文件使用內存,還爲其他內容(例如JVM,GUI等) – m4tx 2012-07-12 10:12:58