GCとページング、GCとソケットバッファ
OLD領域がCG前にページアウトする問題
問題の経緯
- JVM上で動くサーバアプリケーション
- CMS GCを使用
- 状況によってOLD領域に行くオブジェクトがたまに出てくる
- ゆえにOLD領域が埋まり切るまでは長い時間がかかる(フルGCはなかなか実施されない)
- OSによりOLD領域がページアウトされる
- フルGC実行時にページインがGBytes単位で発生する
- アプリケーションが長時間フリーズする
Oracleのページにも書いてあった。
Excessive use of physical memory for Java heap may cause paging of virtual memory to disk during garbage collection, resulting in poor performance.
対策
一番簡単な対策はヒープサイズを縮小することだ。そもそもこの問題は不要に巨大なヒープを割り当てているため発生するものと考えていい。 だが残念ながら今回はたまに訪れる高負荷時に巨大なヒープが必要であり、かつ低負荷の状態が長期間続いてしまったようだ。
しかし対策は簡単である。下記のどちらか(両方でもいい)を実施しよう。
CPUリソースに余裕がないアプリケーションである場合、G1GC を使うと平時のスループットが落ちる可能性が高いが オブジェクトが頻繁にOLD領域へ移るのであれば有用な選択肢だろう。 もちろんこの辺りはアプリケーションの特性や負荷状況に依るのでチューニングしながら試行錯誤する必要はある。 とは言え本番環境と同じような負荷状況がテスト環境に再現できるとは限らないが…。
フルGCとソケットバッファ
フルGCに関して、別の(解決不能な)問題点についても書いておこう。
インバウンドに1Gbpsの通信が発生しているアプリケーションで0.5秒のフルGCが発生した場合、 ソケットバッファから溢れロストするパケットは果たしてどれだけか?これは wmem_max をいくら(現実的な範囲で)大きく指定しようが、 大量のパケット、おそらくは数十MBytes単位がロストするだろう。当然パケットロスに伴いスループットは劇的に悪化する可能性が高い *2が、 GCのある言語を使う以上、これは避けようがない。