Enterprise Java Community: Scaling Your Java EE Applications

Java EE アプリケーションのスケーラビリティについて論じた TSS の論文(ってタイトルそのままだな)。キーポイントが簡潔に纏まっていて読みやすい。

以下、要点のメモ。

ロックにまつわる話

ロックの粒度はとにかく小さくしないとスケールさせられない。そのためにはこんなことに注意が必要。

  • synchronized ブロックは可能な限り小さくしよう。
  • メソッドにキーワード synchronized を付けると暗黙的に「this」がロックされちゃうよ。
  • クラスメソッドを synchronized にすると、そのクラスの全てのインスタンスがロックされちゃうよ。

また、Java 5 の java.util.concurrent.atomic に含まれている「ロックしないデータ構造」を積極的に使おう。こいつらは CAS(compare-and-swap)を使うのでロックが必要なくなる。

ブロッキング I/O とノンブロッキング I/O

ブロッキング I/O だと I/O 処理待ち間もスレッドが保持されてしまうので、並行度が上がってくるとリソースを喰う。ノンブロッキングにすれば I/O 処理待ち中にそのスレッドで別の処理が行えるから、必要なスレッド数は少なくて済む。ノンブロッキング I/O なら CPU 数の増加に伴ってスケールするが、ブロッキング I/O だと CPU が増えても性能は頭打ちになる。

シングルスレッド処理の問題

シングルスレッド処理では CPU の数を増やしても性能は上がらないので、並行処理が可能なように処理を組み替える必要がある。JOMPParallel Java が使えるけどプログラミングは難しい。

メモリ量と GC

「大量のヒープに大量のオブジェクト」という状況では GC 時間がバカにならない。HttpSession にオブジェクトがガンガン突っ込まれていたり、オブジェクトプールやキャッシュがデカすぎたりっていうアプリケーションは要注意。メモリセントリックなアプリケーションの場合は、複数の JVM や複数のマシンを利用しよう。

メモリが潤沢な環境でもヒープは 3GB 程度にして、残りは OS に残しておくべき。そうすれば、OS はそれらのメモリをデータバッファやキャッシュとして使ってくれるため、I/O 処理の改善が期待できる。

他の話題

  • DB に処理が集中するアプリケーションはスケールしない。
  • 同じディレクトリにファイルが死ぬ程あるようなケースだと、OS がボトルネックになるかも。
  • ログを同期処理として出しているアプリケーションはスケールしない。非同期にログ出力すべし。ログサーバを使うとか、JMS でログを遠方に送りつけるとか。