JavaバージョンアップによるGCの変遷（Java 8以降）

現場でJavaアプリケーションのパフォーマンステストを担当することがあり、JavaのGCについて改めて勉強し始めました。

まずはJavaのGCの変遷を理解したいと思い、特に変更が大きかったJava 8と後続バージョンの変更点をまとめています。さくっとJavaのGCの要点だけ押さえたい人向けです。


## Java 8 までのGCの特徴

### 利用可能なガベージコレクタの種類

**シリアルGC**

- 単一スレッドを使用してガベージ・コレクションを実行する
スレッド間通信のオーバーヘッドが発生しないため、効率がいい
- ヒープが最大でも100MB程度の小規模アプリケーションに適している
- server-classマシン（大容量のメモリーと2個以上のプロセッサを搭載）で実行される、スレッド化の程度が非常に高いアプリケーションには向いてない


**パラレルGC**

Java 8のデフォルトのガベージコレクタ。

- 基本的なアルゴリズムはシリアルGCと似ている
主な違いは、複数スレッドを使用してガベージ・コレクションを高速化する点
- マルチプロセッサまたはマルチスレッドで実行される、中規模から大規模なアプリケーションに適している
- ヒープサイズが大きい場合、メジャーGCで長い停止時間が発生する傾向にあり、スループットよりレイテンシーを重視するアプリケーションには向いてない

**CMS GC**

ガベージ・コレクションをアプリケーションと並行して実行できるコンカレント型のコレクタ。Java 9で非推奨となり、Java 14で削除された。

- ガベージ・コレクションによる一時停止時間の短縮が見込めるが、他のガベージコレクタと比べスループットが若干低下する傾向にある
- Old領域のヒープを最適化できず、最終的に長い停止時間を伴うメジャーGCが発生する可能性がある

### メモリ管理

**Metaspace**

Java 7以前に採用されていたPermanent領域はメモリサイズが固定でOutOfMemoryErrorが発生しやすい等、いくつかの課題を抱えていた。動的クラスロードにも不向きでモダンなJVMのニーズに適合しなかったため、Java 8でMetaspaceに置き換えられた。

MetaspaceはJavaヒープ領域外のネイティブメモリに割り当てが行われる。メモリサイズを明示的に指定しない限り、理論上は利用可能なネイティブメモリの上限まで拡大可能。

Metaspaceにはクラスメタデータが格納され、従来Permanent領域に含まれていたインターン化された文字列とクラス静的変数はJavaヒープに格納場所が変更された。その影響により、Java 7以前の実行環境で抑制されていたOutOfMemoryErrorが発生したり、ガベージコレクションの回数が増加したりする可能性がある。

この変更はJRockit と Hotspot VM統合の取り組みの一環らしい。(JRockitにはPermanent領域のような永続的なデータを保存するためのメモリ領域がなかったとか）


## Java 9 以降のGCの主な変更点

### デフォルトのガベージコレクタがG1GCに変更

Java 9以降のデフォルトのガベージコレクタ。

- 大規模なメモリーを保持するマルチプロセッサ・マシンを対象とする
- ガベージ・コレクションによる一時停止時間目標を高い確率で満たそうとする一方、スループットとのバランスの取れたパフォーマンスを実現することを目標に設計されている
- CMS GCと同じくアプリケーションと並行してガベージ・コレクションを実施するコンカレント型

#### 他ガベージコレクタからG1GCへの移行の注意点

https://docs.oracle.com/en/java/javase/17/gctuning/garbage-first-garbage-collector-tuning.html#GUID-0BB3B742-A985-4D5E-A9C5-433A127FE0F6
>Generally, when moving to G1 from other collectors, particularly the Concurrent Mark Sweep collector, start by removing all options that affect garbage collection, and only set the pause-time goal and overall heap size by using `-Xmx` and optionally `-Xms`.

他のガベージコレクタからG1GCに移行する場合、ガベージコレクションに影響するすべてのオプションを削除し、まずは最大ヒープサイズと一時停止時間目標でチューニングすることが推奨されている。

これは、他のガベージコレクターで効果があったチューニングはまったく効果がない、あるいは一時停止時間目標を逆に低下させる可能性があるためである。例としてG1GCには一時停止時間目標を達成するためにYoung領域のサイズを自動調整する機能があるが、`-Xms`や`-XX:NewRatio` で特定の値を設定してしまうと制御が実質無効化されてしまうことが挙げられる。

要するにデフォルト設定でパフォーマンスが出せるように設計されてるため、余計なことはしない方がよいということ。

### 削除されたGCオプション
Java 8で非推奨となった特定のGCオプションの組み合わせが削除された。（これらの GCオプションの組み合わせは、ほとんど使用されていないか、効果が限定的だった）使用した場合はJVMの起動に失敗する。

- DefNew + CMS       : -XX:-UseParNewGC -XX:+UseConcMarkSweepGC
- ParNew + SerialOld : -XX:+UseParNewGC
- ParNew + iCMS      : -Xincgc
- ParNew + iCMS      : -XX:+CMSIncrementalMode -XX:+UseConcMarkSweepGC
- DefNew + iCMS      : -XX:+CMSIncrementalMode -XX:+UseConcMarkSweepGC -XX:-UseParNewGC
- CMS foreground     : -XX:+UseCMSCompactAtFullCollection
- CMS foreground     : -XX:+CMSFullGCsBeforeCompaction
- CMS foreground     : -XX:+UseCMSCollectionPassing

### **Permanent世代の削除**

Permanent領域はJava8で廃止されたため、関連するVMオプションを使用した場合、以下の警告が表示されるようになった。

```
Java HotSpot(TM) 64-Bit Server VM warning: Ignoring option MaxPermSize; support was removed in 8.0
```

- `-XX:MaxPermSize`
- `-XX:PermSize`

### GCログ出力の変更

Java9よりUnified JVM LoggingとしてJVM内部ログの仕様が統一化された。 GC関連のロギングも再実装され、フォーマットや使用するオプションが大きく変わった。

- Java 8以前で使用していたGCログ・パーサーはほぼ変更必須
- GCログの設定が`-Xlog`に統一され、`—XX:+PrintGCDetails`、`-XX:+PrintGC`オプションは非推奨になった

| 出力内容 | Java 8 以前 | Java 9 以降 |
|:--|:--|:--|
|基本的なGCイベントのログ出力| -verbose:gc | -Xlog:gc |
|基本的なGCイベントのログ出力| -XX:+PrintGC | -Xlog:gc |
|詳細なGCイベントのログ出力| -XX:+PrintGCDetails | -Xlog:gc* |
|GCログのリダイレクト| -Xloggc:\<file> | -Xlog:gc*:file=\<file>:time,uptime,level,tags |

### 低レイテンシガベージコレクタの登場

**ZGC**

ZGC は Java 11 で実験的に導入され、Java 15で正式サポートされるようになった。

- 超低レイテンシ: GCの停止時間は10ms未満
- 非常に大きなヒープサイズに対応: 最大16TBのヒープサイズを効率的に管理

**Shenandoah GC**

Shenandoah GC は元々 Red Hat が開発した低レイテンシ志向のGCで、Java 12で導入され、Java 17以降で正式サポートされるようになった。
GC停止時間を最小化するため、移動（コンパクション）フェーズも並行実行という先進的なアルゴリズムを採用しているのが特徴。


## 参考文献
Oracle . Java Platform, Standard Edition Oracle JDK 移行ガイド,リリース17 . Oracle JDK 移行ガイド . 2021 . https://docs.oracle.com/javase/jp/17/migrate/preface.html .