バックグラウンド
ファイルを読み取り (使用しているサンプル ファイルのサイズは約 4 GB です)、ファイルに対して少量の処理を行い、それを Oracle データベースに書き出す Spring バッチ プログラムがあります。
私のプログラムは、1 つのスレッドを使用してファイルを読み取り、12 のワーカー スレッドを使用して処理とデータベースのプッシュを行います。
若い世代のメモリを大量に大量に消費しているため、プログラムが思ったよりも遅くなります。
設定
JDK 1.6.18
Spring バッチ 2.1.x
4 コア マシン、16 GB RAM 搭載
-Xmx12G
-Xms12G
-NewRatio=1
-XX:+UseParallelGC
-XX:+UseParallelOldGC
問題
これらの JVM パラメータを使用すると、Tenured Generation で約 5.x GB のメモリ、Young Generation で約 5.X GB のメモリを取得できます。
この 1 つのファイルを処理する過程で、私の Tenured Generation は問題ありません。最大で 3 GB になるかもしれませんが、完全な GC を 1 回実行する必要はありません。
しかし、ヤングジェネレーションは何度もマックスに達します。それは 5 GB の範囲まで上がり、その後並列マイナー GC が発生し、Young Gen を 500MB の使用量までクリアします。マイナー GC は優れており、完全な GC よりも優れていますが、それでもプログラムの速度が大幅に低下します (データベース アクティビティが停止するため、若い世代のコレクションが発生すると、アプリがまだフリーズしていると確信しています)。プログラム時間の 5% 以上をマイナー GC のために凍結して費やしていますが、これは過剰に思えます。この 4 GB のファイルを処理する過程で、50 ~ 60 GB の若い世代のメモリを大量に消費したと言えます。
私のプログラムには明らかな欠陥は見当たりません。一般的な OO の原則に従い、クリーンな Java コードを作成しようとしています。理由もなくオブジェクトを作成しないようにしています。私はスレッド プールを使用しており、新しいオブジェクトを作成する代わりに、可能な限りオブジェクトを渡しています。アプリケーションのプロファイリングを開始しようとしていますが、過剰なメモリ チャーンを回避するための優れた一般的な経験則やアンチ パターンがあるかどうか疑問に思っていました。4GB のファイルを処理するには、50 ~ 60GB のメモリ チャーンが最適ですか? オブジェクト プーリングのような JDK 1.2 のトリックに戻す必要がありますか? (ただし、Brian Goetz はオブジェクト プーリングが馬鹿げている理由を含むプレゼンテーションを行いますが、もうそれを行う必要はありません。私は自分自身よりも彼を信頼しています .. :) )