こんにちは。篠原です。
普段はJavaを主戦力としている私ですが、巨大なデータの取り扱いが発生する際のプログラムにはいつも苦労します…。
Javaで書いたプログラムはJVMと呼ばれる仮想マシン上で動作するため、JVMが確保しているメモリ領域を超えるサイズのデータをメモリに展開してしまうと、メモリが足りなくなってしまい、エラーが発生します。これがOutOfMemoryエラーです。
解決策としては、データ全量を一気にメモリに展開せず、少しずつメモリ上に読み込んで(バッファリング)、少しずつ十分な容量を持った領域に書き出していく必要があります。
Javaの場合は、 BufferedInputStream BufferedOutputStream を使用することで実現可能です。積極的に使っていきましょう。
最近関わっている案件では、Springフレームワークで作成したWebアプリケーションから、巨大なファイルをWebAPI経由で別サーバーに中継する必要がありました。
Springフレームワークの素晴らしいところは、単純なWebフォームを通したデータ伝送の場合、受け取ったリクエストデータは自動的に一時ファイルとして受け取ってくれるところ( MultipartFile 型で受け取ったデータの実体は、サーバー上の一時ファイルです)。
そこまでは完璧なのですが、そのファイルを RestTemplate を使用して別サーバーへHTTPリクエストに載せようとしたところ、OutOfMemoryエラーが発生。
リクエストボディに書き出す際に、デフォルトでは ByteArrayOutputStream が使用されているらしいのです。
ファイルを伴わない一般的なHTTPリクエストであれば、いちいちバッファリングを伴わない方がパフォーマンスが上がるのかもしれませんが、ちょっとショック…。
ここでも同様に、少しずつリクエストを書き込むよう改修を行って解決を図ることになりました。
(本来はリクエストを書き出した後も、チャンク形式とするなども考慮しないといけませんが、割愛します)
あらためて、メモリ容量を意識しながらデータを取り扱うことの大切さを実感したよい機会でした。