初めに
組み込み系エンジニアの友人と飲んでいた際、C言語ではメモリをユーザが意図的に開放しないとメモリリークが起きるということを教えてもらいました。
自分は普段Javaを書いてますが、メモリを意識してコーディングをしたことはほとんどありません。どのような仕組みになっているのか気になったため、Javaにおけるメモリ管理について調べてみました。
Javaではメモリ管理は誰がやってるの?
CやC++ではユーザが意図的にメモリ管理をする必要がありました。JavaではJVMがその役割を担っています。メモリの使用や解放を自動で行ってくれるため、ユーザがメモリを意識せずにコーディングできるというわけです。
メモリの種類
Javaプログラムが実行されると、Javaのプロセスにメモリが割り与えられます。そのメモリ領域は以下の4つの領域に分けられます。
- ヒープ領域
- スタック領域
- メタスペース
- ネイティブメモリ(※JVMではなくOSが管理する領域)
領域 | 用途・特徴 |
---|---|
ヒープ領域 | 生成されたインスタンスの情報が保存される領域。 |
スタック領域 | 実行中のプログラムの情報が保存される領域。 実行中の行が「どこから呼び出されていて、どんな情報を参照可能か」などを持っているイメージ。 |
メタスペース | クラス定義、メソッドコード、定数プールなどのクラス関連の情報が保存される領域。 |
ネイティブメモリ | Javaプログラムがネイティブコード(C、C++などで記述されたコード)を呼び出す際に使用される領域。 JVMではなくOSが直接管理をしている。 |
上記4つのうち、プログラム実行中にメモリの管理が必要なのはヒープ領域のみです。
メモリ管理方法をみていきましょう。
ヒープ領域のメモリ管理
ヒープ領域のメモリが解放される仕組みを知るには、ヒープ領域の構造とガベージコレクションについて知る必要があります。
ヒープ領域の構造
ヒープ領域は大きく分けて以下の2つの領域で構成されています。
- Young:メモリに格納されてからの時間が比較的短いデータが格納されている領域。
- Old:メモリに格納されてからの時間が比較的長いデータが格納されている領域
Young領域にはさらにEdenとSurvivorの2つの領域に分けられます。
Survivorは領域が2つありますが、どちらも役割は同じです。
ガベージコレクション(GC)とは
ガベージコレクション(GC)とは、不要になったメモリを解放するJVMの機能です。
ヒープ領域を対象として不要になったメモリの解放を行います。
GCは主に以下の2種類があります。
- マイナー GC:Young領域を対象としたメモリ解放
- Full GC:ヒープ領域全体を対象としたメモリ解放
それぞれのメモリ解放の流れを説明します。
以下の記事がわかりやすかったので参考にさせていただきました。
https://qiita.com/e_tyubo/items/48398391a8ef0f24c1be
マイナーGCによるメモリ解放の流れ
マイナーGCは、Edenがいっぱいになったタイミングで発生します。
その際、以下のルールでデータを処理してメモリを解放します。
- Edenのデータ
- データが不要(参照されてない)場合、削除してメモリを解放する
- データがまだ必要な場合、Survivorに移動させてEdenを解放する
- Survivorのデータ
- データが不要の場合、削除してメモリを解放する
- データがまだ必要な場合、もう片方のSurvivor or Oldに移動させる(一定回数以上Survivorを行き来した場合はOld)
具体的に図で見ていきましょう。
EdenがいっぱいになるとマイナーGCが起きます。
その際、Edenのまだ必要なデータはSurvivorに移動されます。
これでEdenのメモリがすべて解放されました。
その後、Edenのデータがたまっていくと再度マイナーGCが起きます。
Survivorにあるデータは不要になっていた場合解放されますが、まだ必要な場合はもう片方のSurvivorに移動されます。
これを繰り返すとSurvivorにあるデータは2つのSurvivor領域を行き来することになります。そして、行き来した回数が一定数を超えるとOld領域に移動されるというわけです。
Full GCによるメモリ解放の流れ
マイナーGCを繰り返していると、Oldにデータがたまっていくことになります。
Old領域がいっぱいの状態でマイナーGCを実行すると、SurvivorからOldへデータを移動させようとしたタイミングで失敗します。
その失敗を契機として実行されるのがFull GCです。
Full GCでは、YoungとOldの両方に対してメモリ解放を行います。Young領域に対してはマイナーGCと同じルールで実施し、Oldについては不要になったメモリを解放するというシンプルなルールです。
おわりに
今回はJVMにおけるメモリ管理についてまとめました。
Javaを書いているとメモリ管理を意識する機会は少ないですが、知っておいて損はないと思います。
参考
https://qiita.com/e_tyubo/items/48398391a8ef0f24c1be
https://itpfdoc.hitachi.co.jp/manuals/3020/30203M0461/EM040178.HTM
https://phoeducation.work/entry/20210212/1613088000
https://qiita.com/odekekepeanuts/items/b87bff49565fc9abc91b
https://doc.support-dreamarts.com/%E3%81%B2%E3%81%B3%E3%81%8DSm@rtDB/V45/%E3%81%B2%E3%81%B3%E3%81%8DSm@rtDB_Ver.4.5_%E9%81%8B%E7%94%A8%E3%82%AC%E3%82%A4%E3%83%89/appendix/914000/index.html
http://msugai.fc2web.com/java/perform/storage.html