2026年2月11日水曜日

【Java進化史 第10回】Java8 〜あのPermGenはどこへ消えた〜

【Java進化史 第10回】Java8 〜あのPermGenはどこへ消えた〜

昔、意味も分からず書いていたオプションがある。


-XX:MaxPermSize=256m

足りなければ増やす。
512m。
1024m。

それでも、死ぬ。


java.lang.OutOfMemoryError: PermGen space

あの文字列を、何度見ただろう。

■ PermGenとは何だったのか

Java7まで、JVMのメモリは大きくこう分かれていた。


Heap
 ├─ Young領域
 ├─ Old領域
 └─ PermGen(Permanent Generation)

PermGenはヒープの一部で、主に

  • クラスのメタ情報
  • メソッド情報
  • 静的変数
  • (Java6までは)文字列定数プール

を保持していた。

そして最大の問題は、 サイズが固定だったことだ。

ヒープのように自動で伸びない。
だから溢れる。

■ Stringを多用するな、と言われた時代

あの頃、よく言われた。

「Stringを多用するな」

理由は曖昧だった。
だが確かにサーバはよく死んだ。

これは半分本当だ。

Java6までは、文字列定数プールがPermGenにあった。

大量の文字列リテラル、internの多用、
フレームワークが内部で生成する文字列。

それらがPermGenを圧迫することがあった。

だから、

「Stringがヤバい」

という空気は、間違いではなかった。

■ でも本質はStringそのものではない

問題は設計だった。

固定サイズのPermGenに、 クラス情報と文字列プールを押し込んでいたこと。

さらに、Stringは不変(immutable)。


String s = "";
for (int i = 0; i < 10000; i++) {
    s += i;
}

このコードは、大量の中間Stringを生む。

だから我々は学んだ。

「StringBuilderを使え」

間違ってはいない。
だが根本原因はJVM側の設計にもあった。

■ GCパラメータ職人の時代

最後は、GCと戦っていた。

レスポンスが急に悪くなる。
CPUが跳ね上がる。
サーバが固まる。

Full GC。

Stop The World。

ログを吐かせる。


-XX:+PrintGCDetails
-XX:+UseConcMarkSweepGC
-XX:NewRatio=2
-XX:SurvivorRatio=8

意味を完全に理解していたか?

正直、怪しい。

だが、体で覚えていた。

パラメータを調整し、 再起動し、 負荷試験をし、 延命する。

今思えば、 あれは完全に職人技だった。

■ Java7で起きた静かな改善

Java7で文字列プールはヒープへ移動した。

PermGenに直接効くString問題は緩和された。

だが、PermGenそのものはまだ存在していた。

■ Java8でPermGenは消えた

Java8でPermGenは廃止された。

代わりに導入されたのが Metaspace

  • ヒープ外(ネイティブメモリ)を使用
  • 原則、動的に拡張

固定枠で突然死ぬ設計ではなくなった。

さらにGCも成熟した。

「あれ?最近安定してない?」

それは気のせいではない。

  • PermGen廃止
  • Metaspaceの動的拡張
  • GCアルゴリズムの成熟

足元が、ちゃんと進化していた。

■ あの頃の自分へ

-XX:MaxPermSizeを書き、
Stringを疑い、
GCログを読み、
サーバの負荷に怯えていた自分へ。

PermGenは、もういない。

Java8は文法革命だけではない。

JVMの土台も、ちゃんと進化していた。

少しだけ、戦わなくてよくなった。

ほんと、助かるよ。





0 件のコメント:

コメントを投稿

【Java進化史 第10回】Java8 〜あのPermGenはどこへ消えた〜

【Java進化史 第10回】Java8 〜あのPermGenはどこへ消えた〜 昔、意味も分からず書いていたオプションがある。 -XX:MaxPermSize=256m 足りなければ増やす。 512m。 1024m。 それでも、死ぬ。 java.l...