私は、データベースから100万行を読み取り、それらをファイルに書き込む単純なJavaプログラムを作成しました。
このプログラムが使用できる最大メモリは512Mです。
このプログラムが500K行を超えるメモリ不足で実行されていることによく気づきます。
このプログラムは非常に単純なプログラムであるため、メモリリークがないことが簡単にわかります。プログラムが機能する方法は、データベースから1000行をフェッチし、Streamsを使用してそれらをファイルに書き込み、次の1000行をフェッチすることです。各行のサイズは異なりますが、どの行も巨大ではありません。プログラムの実行中にダンプを取ると、古い文字列がヒープ上に簡単に表示されます。ヒープ内のこれらの文字列には到達できません。つまり、ガベージが収集されるのを待っています。また、このプログラムの実行中にGCが必ずしも実行されるとは限らないため、Stringがヒープ内に必要以上に長く残ると思います。
解決策は、DBによって返される行を格納するためにStringオブジェクトを使用する代わりに、長いChar配列(またはStringbuffer)を使用することだと思います。Char配列の内容を上書きできると想定しています。つまり、毎回新しいスペースを割り当てることなく、同じChar配列を複数の反復で使用できます。
擬似コード:
- new char[1000][1000]を使用して配列の配列を作成します。
- DBからアレイへの1000行を埋めます。
- 配列をファイルに書き込みます。
- 次の1000行に同じ配列を使用する
上記の擬似コードで問題が解決した場合、文字列が使用されなくなったとしても、文字列が使用したスペースを直接要求する方法がないため、実際には文字列クラスの不変の性質がJavaプログラマーを傷つけます。
この問題に対するより良い代替案はありますか?
PS:私は静的分析だけをしませんでした。yourkitプロファイラーを使用してヒープダンプをテストしました。ダンプには、文字列の96%にGCルートがないことが明確に示されています。これは、ガベージコレクションを待機していることを意味します。また、コードではサブストリングを使用していません。