...しかし、コピー操作中のある瞬間に約160MBのヒープスペースが必要であることがわかりました
これは非常に驚くべきことだと思います...ヒープの使用量を正しく測定しているとは思えないほどです。
コードが次のようなものであると仮定しましょう。
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("somefile"));
ByteArrayOutputStream baos = new ByteArrayOutputStream(); /* no hint !! */
int b;
while ((b = bis.read()) != -1) {
baos.write((byte) b);
}
byte[] stuff = baos.toByteArray();
ByteArrayOutputStream がそのバッファーを管理する方法は、初期サイズを割り当て、(少なくとも) いっぱいになったときにバッファーを 2 倍にすることです。したがって、最悪の場合baos
、40Mb のファイルを保持するために最大 80Mb のバッファーを使用する可能性があります。
最後のステップでは、正確にバイトの新しい配列を割り当ててbaos.size()
、バッファーの内容を保持します。それは40Mbです。したがって、実際に使用されているメモリのピーク量は 120Mb になるはずです。
では、これらの余分な 40Mb はどこで使用されているのでしょうか? 私の推測では、それらはそうではなく、実際には、到達可能なオブジェクトが占有するメモリの量ではなく、合計ヒープ サイズを報告していると思います。
それで、解決策は何ですか?
メモリ マップド バッファを使用できます。
ByteArrayOutputStream
;を割り当てるときにサイズのヒントを与えることができます。例えば
ByteArrayOutputStream baos = ByteArrayOutputStream(file.size());
完全に省略してByteArrayOutputStream
、バイト配列に直接読み取ることができます。
byte[] buffer = new byte[file.size()];
FileInputStream fis = new FileInputStream(file);
int nosRead = fis.read(buffer);
/* check that nosRead == buffer.length and repeat if necessary */
オプション 1 と 2 の両方で、40Mb ファイルの読み取り中のピーク メモリ使用量は 40Mb になるはずです。つまり、無駄なスペースはありません。
コードを投稿し、メモリ使用量を測定する方法を説明していただけると助かります。
ByteArrayOutputStream を拡張してこのメソッドを書き直して、元の配列を直接返すことができると考えています。ストリームとバイト配列が複数回使用されない場合、ここに潜在的な危険はありますか?
潜在的な危険は、あなたの仮定が間違っているか、他の誰かがあなたのコードを無意識のうちに変更したために間違っていることです...