3
3

【Java】JVMにおけるメモリ管理とガベージコレクションについて

Last updated at Posted at 2023-11-05

初めに

組み込み系エンジニアの友人と飲んでいた際、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つありますが、どちらも役割は同じです。

image.png

ガベージコレクション(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に移動されます。

image.png

これでEdenのメモリがすべて解放されました。

その後、Edenのデータがたまっていくと再度マイナーGCが起きます。
Survivorにあるデータは不要になっていた場合解放されますが、まだ必要な場合はもう片方のSurvivorに移動されます。

image.png

これを繰り返すとSurvivorにあるデータは2つのSurvivor領域を行き来することになります。そして、行き来した回数が一定数を超えるとOld領域に移動されるというわけです。

image.png

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

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3