3

私はサーバーとクライアントのアプリケーションを動作させました。小さなファイルを送信するときは完璧に動作しますが、たとえば、ソケットを介して 700 MB のムービー ファイルを送信しようとすると、

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

私はインターネットを検索し、大きなファイルを送信するためのチュートリアルをいくつか見つけましたが、それらを完全に理解することはできませんでしたが、私の問題はファイルの書き込みにあると思います.

これは、サーバーがファイルを書き込むために使用するコードです。

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

5 に答える 5

7

ファイルをOutOfMemoryError送信する前にファイル全体をメモリに読み込もうとしているためです。これは 100% 完全に不要です。受信コードで行っているのと同じように、チャンクを読み書きするだけです。

于 2012-07-12T10:02:48.567 に答える
4

問題は、ファイル全体を一度にメモリにロードしようとしていることです (を介してreadFully)。これは、ヒープ領域 (デフォルトでは 256mb だと思います) を超えています。

次の 2 つのオプションがあります。

  1. 適切なオプション: ファイルをチャンク (一度に 1024 バイトなど) でロードし、そのように送信します。実装にはもう少し手間がかかります。
  2. 悪いオプション: -Xmx オプションでヒープ領域を増やしてください。お勧めしません。これは主に、プログラムにもう少しヒープ領域が必要な場合に備えて言及しましたが、この場合は本当に悪い考えです。

オプション 1 では、次のようなものを試すことができます。

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();
}
于 2012-07-12T10:03:12.607 に答える
2

OutOfMemoryErrorプログラムがヒープ サイズの制限に達するとスローされます。ファイル全体を RAM にロードします。デフォルトのヒープ サイズは物理メモリ サイズの 1/4 または 1GB であるため、プログラムは制限に達します (おそらく 2GB の RAM があるため、ヒープ サイズは 512MB です)。

ヒープ サイズの制限に達しないように、ファイルをチャンク (たとえば 10MB) で読み取る必要があります。エラーが発生した場合は、ファイル全体を再送信する代わりに、チャンクを再送信できます。別のスレッドでチャンクを読み取ることもできるため、1 つのチャンクを送信するときに 2 番目のチャンクをロードし、最初のチャンクを送信したらすぐに 2 番目のチャンクの送信を開始できます。

于 2012-07-12T10:10:42.417 に答える
1

問題は、クライアントでファイル全体のバイト配列を作成することです。

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

サーバー側で行うのと同じことを行い、ファイルをチャンクごとに固定サイズのバッファーに読み込む必要があります。

于 2012-07-12T10:04:15.277 に答える
0

サーバー側でバッファを使用してデータを送信するのと同じように、アプレット/クライアントでバッファを使用してデータを読み取ります。大きなファイルを転送している場合、使用すると明らかにメモリ不足になりますreadFully()この方法は、データが非常に小さいことがわかっている場合にのみ役立ちます。

于 2012-07-12T10:05:44.637 に答える