Javaのヒープ・メモリ管理の仕組み

この記事では,Javaのヒープ・メモリ管理の仕組みについてまとめる。参考にした記事を自分なりに解釈しやすいように読み替えながら書いた。

前提知識

ヒープ

OSやアプリケーションソフトが使用するメモリ領域の一種。用途に関係なく自由に確保することができる。OSからはヒープメモリを確保した位置のアドレスが渡され,アプリケーションソフトはこの値を元にして確保したヒープ領域を使用する。ヒープメモリはアプリケーション側での自由度が高い反面,ガベージコレクション機能がないプログラミング言語(の処理系)では,確保したヒープをすべて手作業で解放しなければならないため,誤解放や解放漏れなどのミスが起こりやすい。このヒープの解放漏れが「メモリリーク」である。

JVM

JavaはJVM(Java Virtual Machine)と呼ばれる仮想マシン上で動作している。APIやいくつかのツールとセットでJava実行環境 (JRE) としてリリースされている。この環境を移植することで,さまざまな環境でJavaのプログラムを実行することができる。

ガベージ・コレクション

Javaではヒープ上のメモリ領域をJavaオブジェクトに割り当てたり,もしくは割り当て済みの領域を解放したりする処理をプログラムコードに明示的に記述する必要がない。CやC++ではプログラムが使用するメモリ領域の割り当てや使用済みの領域の解放をプログラムコードから明示的に指示しなくてはならない。Javaではこうしたメモリ管理がJVMによって自動的に行われている。JVMはJavaプログラムのどこからも参照されなくなった不要なJavaオブジェクトを見つけ出し,そのメモリ領域を自動的に解放する。こうしたJavaオブジェクトの削除処理は「ガベージ・コレクション」と呼ばれる。

JVMで使用するメモリ空間の構成

メモリ空間の具体的なイメージを図で表すと以下のようになる。

8e3db29a32bf8b71c6bfe29a67065911

Eden領域

new演算子によって作成されたJavaオブジェクトが最初に格納される領域である。

Survivor領域

New領域に格納されていたJavaオブジェクトのうち,ガベージ・コレクション実行時に破棄されなかったJavaオブジェクトが格納される領域である。Survivor領域は,From空間とTo空間で構成される。From空間とTo空間のサイズは同じ。

Tenured領域

長期間必要であると判断されたJavaオブジェクトが格納される領域である。Survivor領域で指定回数を超えてガベージ・コレクションの実行対象となり,破棄されなかったJavaオブジェクトが,この領域に移動される。

Permanent領域

ロードされたclassなどの情報が格納される領域である。

Cヒープ領域

JVM自身が使用する領域である。

スタック領域

Javaスレッドのスタック領域である。

JVM固有空間のヒープ・メモリの構造について

Javaにおけるガベージ・コレクションのメカニズムを理解するには,ヒープ・メモリの構造を知っておく必要がある。JVM固有空間には大きく分けて以下の2つの領域がある。

  • New領域
  • Old領域

New領域には新しいオブジェクトが格納され,Old領域には古いオブジェクトが格納される。生まれてすぐに不要となる短命なオブジェクトはNew領域でその一生を過ごし,長時間存在するオブジェクトはOld領域に留まることになる。

以下はJVMにおけるヒープ・メモリの構造を示したものである。

a3fdf062fc481501f9e4aa8318a75258

JVMでは「Scavenge GC」と「Full GC」という2種類のガベージ・コレクションが実行される。Scavenge GCはNew領域のみを対象とした短時間で終了するガベージ・コレクションであり,頻繁に実施される。一方,Full GCはNew領域とOld領域,両方の領域を対象とした大がかりなガベージ・コレクションであり,比較的低い頻度で実施される。こうした理由から,ヒープ・メモリ全体がオブジェクトの世代別に分割されている。

JVMのデフォルト設定では,New領域の起動時のサイズが2MB,最大サイズが16MBにセットされている。また,Old領域の起動時のサイズは4MB,最大サイズは48MBである。これらのサイズは,以下のJVMオプションを用いることで変更することができる。

オプション 説明
-Xms ヒープ・メモリ全体の起動時のサイズ
-Xmx ヒープ・メモリ全体の最大サイズ
-Xmn NEW領域のサイズ
-XX:SurvivorRatio= Eden領域のサイズをFromまたはTo領域のサイズで割った値(FromとTo領域は同じサイズ)

※最初の3種類のオプションについては指定するサイズをメガバイト単位で指定する。例えば「-Xmn256m」はNew領域に256MBを割り当てることを意味する。

JVMにおけるガベージ・コレクションのメカニズム

New領域はさらに「Eden」「From」「To」という3つの領域に分割されている。これらのうち,Eden領域は,新しいオブジェクトが作成された際に最初に配置されるメモリ領域である。Eden領域は,時間が経つとともに新しいオブジェクトで埋め尽くされていく。Eden領域が満杯になると,前述したScavenge GCが実行される。このとき,まだ使用中のオブジェクト(すなわち,他のオブジェクトから参照されているオブジェクト)についてはEden領域からTo領域へと移動され,参照されていない不要なオブジェクトは破棄される。これにより,Eden領域全体がクリーンアップされ,再び新しいオブジェクトを受け入れ可能となる。

87d763c5095350d1ea9b1964affc5783

ここで,To領域に移動されたオブジェクトには「1」という数値が振られる。これはScavenge GCによってオブジェクトが移動した回数を表している。この数値の意味については追って説明する。

続いて,Eden領域が再度満杯になり,2回目のScavenge GCが実行される状況を考える。このとき,前回のGC時のFrom領域とTo領域は互いに入れ替わることになる。つまり,前回のGCでオブジェクトの移動先とされたTo領域は次回のGCではFrom領域として扱われるわけだ。

そして,このFrom領域とEden領域にある使用中のオブジェクトが再び行われるガベージ・コレクションを生き残るとTo領域に移動される。

このようにScavenge GCではオブジェクトが不要になるまでの間,From領域とTo領域の間でオブジェクトの移動を繰り返す。そして,この移動の際にはオブジェクトに振られた移動回数の数値がカウントアップされる。

Scavenge GCではこの移動回数が「MaxTenuringThreshold」と呼ばれる閾値を上回るオブジェクトについてOld領域への移動を行う。

95588390184a92544a79b4777b4b7bd6

JVMにおけるMaxTenuringThresholdのデフォルト値は32に設定されている。よって新しいオブジェクトは,最大で32回までScavenge GCの対象となり,その間をFrom領域とTo領域の中で過ごす。この回数を超えて生き延びたオブジェクトは「寿命の長いオブジェクト(tenured object)」としてOld領域に移動される仕組みになっている。

スタック領域について

一番初めに示した図の「スタック領域」について詳しく説明する。スタック領域ではローカル変数が管理されている。ローカル変数とはメソッドやメソッド内のfor,if等のスコープで定義される変数のことでスタック変数とも呼ばれる。
プログラムを実行中にメソッド呼び出しが行なわれるとPermanent領域に格納されているクラス情報からメソッド内容を読み込み,スタック領域に展開して処理を実行していく。スタック領域に積まれる単位は(メソッド,for,if等の)処理スコープ単位である。なお,スコープ内の処理が全て完了すると,スタック領域に積まれた内容は破棄される。メソッド内で宣言した変数やfor文内で宣言した変数が,そのスコープを脱出すると利用できなくなる理由はこのスタック領域の管理仕様によるものだ。

75d30680a6f15d215d95ad6a7cc22069

「プリミティブ型」と「参照型」はメモリ管理の仕組みが異なる

変数には大きく分けて「プリミティブ型」と「参照型」の2つが存在する。そして,プリミティブ型と参照型ではメモリ管理の仕組みが異なる。
プリミティブ型では値がスタック領域に直接格納される。対して参照型ではスタック領域にヒープの参照情報(アドレス)が格納される。

f757a14475b51d515601a90643d9946f

参照型の変数はPermanent領域に格納された情報からヒープ領域へインスタンスの展開を行い,参照情報(アドレス)を元にヒープ領域に展開されたインスタンスへアクセスする。

参考
ヒープとは – 意味/解説/説明/定義 : IT用語辞典
JVMとGCのしくみ – ITエンジニアとして生きる
第7回 HotSpot JVMではどのようにオブジェクトが移動するのか:Javaはどのように動くのか~図解でわかるJVMの仕組み|gihyo.jp … 技術評論社 Java:意外と教わる機会の少ないメモリ管理のお話(4) – omotenashi-mind
技術の広場 – Javaの知られざる欠陥(上):ITpro
JavaVMで使用するメモリ空間の構成とJavaVMオプション
チューニングのためのJava VM講座(前編):Hotspot VMの基本構造を理解する (1/2) – @IT
Javaパフォーマンスチューニング(6):HotSpot VMの特性を知る (1/2) – @IT
JVMのメモリ管理 – やさしいデスマーチ