サーバーから260MBの大きなファイルをダウンロードしようとすると、次のエラーが発生します:Java.lang.OutOfMemoryError: Java heap space.
ヒープサイズは252MB未満だと確信しています。ヒープサイズを大きくせずに大きなファイルをダウンロードする方法はありますか?
この問題が発生せずに大きなファイルをダウンロードするにはどうすればよいですか?私のコードを以下に示します。
String path= "C:/temp.Zip";
response.addHeader("Content-Disposition", "attachment; filename=\"test.Zip\"");
byte[] buf = new byte[1024];
try {
File file = new File(path);
long length = file.length();
BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
ServletOutputStream out = response.getOutputStream();
while ((in != null) && ((length = in.read(buf)) != -1)) {
out.write(buf, 0, (int) length);
}
in.close();
out.close();
メモリ使用量が増加している可能性があることがわかる場所が2つあります。
#1の場合、FileInputStream
を使用せずにBufferedInputStream
を介してファイルから直接読み取ることをお勧めします。最初にこれを試して、問題が解決するかどうかを確認してください。すなわち:
FileInputStream in = new FileInputStream(file);
の代わりに:
BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
#1で問題が解決しない場合は、大量のデータが書き込まれた後、出力ストリームを定期的にフラッシュしてみてください(必要に応じてチャンクサイズを減らしてください)。
すなわち:
try
{
FileInputStream fileInputStream = new FileInputStream(file);
byte[] buf=new byte[8192];
int bytesread = 0, bytesBuffered = 0;
while( (bytesread = fileInputStream.read( buf )) > -1 ) {
out.write( buf, 0, bytesread );
bytesBuffered += bytesread;
if (bytesBuffered > 1024 * 1024) { //flush after 1MB
bytesBuffered = 0;
out.flush();
}
}
}
finally {
if (out != null) {
out.flush();
}
}
残念ながら、out
のタイプについては触れていません。メモリに問題がある場合は、ByteArrayOutpoutStream
だと思います。したがって、それをFileOutputStream
に置き換えて、ダウンロードするバイトをファイルに直接書き込みます。
ところで、バイトごとに読み取るread()
メソッドは使用しないでください。代わりにread(byte[] arr)
を使用してください。これははるかに高速です。
まず、whileステートメントから(!= null内)を削除できます。これは不要です。次に、BufferedInputStreamを削除して、次のようにします。
FileInputStream in = new FileInputStream(file);
表示されているコードには(メモリ使用量に関して)何も問題はありません。サーブレットコンテナが応答全体をバッファリングするように構成されているか(web.xml
構成を参照)、メモリが他の場所でリークされています。