Javaのプログラミング言語を学習したことがある方の中には、ガベージコレクションの名前は知っているけれども機能についてはあまり把握出来ていない方も多いのではないでしょうか。
本記事では、ガベージコレクションの基本的な仕組みについて、初心者向けにご紹介していきます。
ガベージコレクションとは
ガベージコレクションは、使用されなくなったメモリ領域を判別し、自動的に再利用する仕組みのことを指します。
Javaのガベージコレクション
Java言語では、ガベージコレクションを開発者が明示的に指示する必要がなく、JVM(JavaVirtualMachine)が自動的に行います。
JVMが自動的に必要なメモリ領域と不要なメモリ領域を判断してくれるため、基本的には何も意識しなくても良いのですが、必要に応じて開発者が最適なガベージコレクションを行ってもらうようチューニング(調整)を行います。
Scanvenge GCとFull GC
ガベージコレクションには大きく分けると「scanvenge GC(スキャンベンジ・ガベージコレクション)」と「Full GC(フル・ガベージコレクション)」の2種類が存在します。
Scanvenge GC
Scanvenge GCは簡単にいうと、特定領域だけを対象としたガベージコレクションで短時間での作業が完了します。
メモリ領域には、すぐに廃棄されるオブジェクト用の「New領域」と寿命の長いオブジェクト用の「Old領域」に分けられます。
Scanvenge GCで対象となるのは「New領域」のみです。
Full GC
Full GCは名前の通り、メモリ領域の全てをガベージコレクションする機能です。
Scanvenge GCでは「New領域」のみが対象と上述しましたが、Full GCでは「Old領域」も含めて対象となります。
対象範囲が広いことからScanvenge GCよりも処理の完了までに時間が掛かります。
ガベージコレクションの仕組み
ガベージコレクションとは何なのかについてある程度紹介出来たので、次は仕組みについて確認していきましょう。
ガベージコレクションの仕組みを理解するためには、Javaプログラムの仕組み自体を把握しておくことが大切です。
JVMとヒープ領域
Javaでは、オブジェクト指向言語と言われる通り、オブジェクトをJVMのメモリ領域に確保して処理を実行します。
このメモリ領域のことを「ヒープ領域」と呼びます。
従来のC言語やC++言語などでは、このヒープ領域は一度オブジェクトを利用するために確保すると、プログラム上では利用されていないとしても明示的に解放しないと自動で解放されることはありませんでした。
しかしJava言語では、JVM上に確保されたヒープ領域の使用可否を判断して自動で解放処理が行われるようになっています。
ヒープ領域の分割
少し前にも触れていますが、ヒープ領域には大きく「New領域」と「Old領域」が存在します。
文字通りデータとして存在期間が短いデータは「New領域」長いデータは「Old」領域に分類されます。
基本的な考えとして、長期的に参照されるようなオブジェクトは多くないと捉えられており、「New領域」を中心にガベージコレクション対象として調査する方が効率的です。
ガベージコレクションのアルゴリズム
ガベージコレクションと一言で表しても、実は内部のアルゴリズムは利用環境や言語のバージョンなどにより異なります。
- シリアル型GC
- パラレル型GC
- コンカレント型GC
シリアル型GC
全てのアプリケーションスレッドを停止して、スレッド1つでガベージコレクションを実施します。
最もシンプルな方法ですが、ガベージコレクションの対象となるオブジェクトが増えるほどアプリケーションの停止時間も長くなります。
パラレル型GC
パラレル型CGでは、全てのアプリケーションスレッドを停止するのはシリアル型と同じですが、複数のスレッドでガベージコレクションを実施します。
ガベージコレクションを行うスレッドが増えることにより、アプリケーションの停止時間が短くなります。
コンカレント型GC
コンカレント型GCは、アプリケーションを停止せずにガベージコレクションを実施する手法です。
アプリケーションスレッドを止めない分負荷が掛かるため、ガベージコレクションの時間は掛かりますが、アプリケーションを止める時間が少ないメリットがあります。
ただし、完全にアプリケーションを停止せずに完了出来るわけではないようで、ガベージコレクションの都合上短時間のみアプリケーションが停止する可能性はあるようです。
コンカレント型GCにもいくつかの種類が存在し、Java8などではCMS GC、Java11からはG1 GCと呼ばれるアルゴリズムがデフォルトになっています。
Javaアプリでガベージコレクションの影響を少なくするには
Javaアプリケーションではコンカレント型GCが利用されていれば、レスポンス遅延が発生する可能性はあっても、アプリケーション自体が止まることは滅多にありません。
しかしFull GCが発生してしまった場合には、アプリケーションの停止に繋がりますのでFull GCを回避するための対策についてもご紹介しておきます。
New領域を広げる
New領域を拡大することで、Old領域への割り当てを少なくし、効率的にガベージコレクションを実施するための手法です。
New領域は最大ヒープサイズに対して、3分の1から2分の1程度が推奨されています。
Full GCを回避するための対策を行うのであれば2分の1程度を目安に設定してみましょう。
New領域の指定コマンドは「-Xmn」オプションです。
例えば480メガバイトの最大ヒープサイズに対して、積極的にNew領域を確保するのであれば、下記のような記述で240メガバイト確保してしまうなどが検討出来ます。
java -Xmn240m -Xmx480m
-Xmxは最大ヒープサイズを指定するオプションコマンドです。
他にも最小ヒープサイズを指定する-Xmsなどが利用出来ます。
オブジェクトの使いまわしを避ける
上述しているように、オブジェクトの存在時間が長いものはOld領域に割り当てられます。
つまりガベージコレクションの対象としてチェックする機会が少なくなり、無駄なメモリ確保に繋がります。
オブジェクトはなるべくNew領域で解放されるように意識することで、効率的にガベージコレクションが実施され、Full GCをしなくても良い状態を保つことが可能です。
さいごに: Javaのガベージコレクションは開発者の味方
本記事では、Javaのガベージコレクションについて、基本的な内容をご紹介してきました。
開発者レベルであれば、ガベージコレクションのチューニングを行う機会は稀であり、ややこしいなと感じたとしてもJVMが自動で実施してくれる機能なので、最低限の知識だけ抑えておけば問題になるようなことは滅多にありません。
初心者の方には少し難しい内容かも知れませんが、まずはガベージコレクションがどういった機能なのかを把握して、こういった恩恵によりJavaでのシステム開発が簡単に行えているんだということを理解しておきましょう。
New領域はYoung領域とも呼ばれたりしています。
ここでの説明はあくまで概要に留めており、詳細については省略しています。