以前、私はJava Garbage CollectionとJava is Pass by Valueについていくつかの投稿を書きました。その後、Java Heap Space、Java Stack Memory、Javaのメモリ割り当てとそれらの違いについて説明するための多くのメールを受け取りました。Java、Java EEの書籍、チュートリアルでは、Javaのヒープメモリとスタックメモリについてプログラムの観点から完全な説明はほとんどありません。
Java Heap Space
Java Heap Spaceは、JavaランタイムがオブジェクトとJREクラスにメモリを割り当てるために使用されます。オブジェクトを作成するとき、常にヒープ領域に作成されます。ガベージコレクションは、参照を持たないオブジェクトによって使用されるメモリを解放するために、ヒープメモリ上で実行されます。ヒープ領域に作成されたオブジェクトは、グローバルアクセスを持ち、アプリケーションのどこからでも参照できます。
Java Stack Memory
Javaのスタックメモリはスレッドの実行に使用されます。スタックメモリには、メソッド固有の短期間の値とメソッドから参照されるヒープ内の他のオブジェクトへの参照が含まれています。スタックメモリは常にLIFO(Last-In-First-Out)の順序で参照されます。メソッドが呼び出されるたびに、メソッドがローカルなプリミティブ値とメソッド内の他のオブジェクトへの参照を保持するためにスタックメモリに新しいブロックが作成されます。メソッドが終了すると、ブロックは使用されなくなり、次のメソッドに使用するために利用可能になります。スタックメモリのサイズはヒープメモリに比べて非常に少ないです。
Javaプログラムにおけるヒープメモリとスタックメモリ
以下のプログラムを使用して、ヒープメモリとスタックメモリの使用方法を理解しましょう。
package com.journaldev.test;
public class Memory {
public static void main(String[] args) { // Line 1
int i=1; // Line 2
Object obj = new Object(); // Line 3
Memory mem = new Memory(); // Line 4
mem.foo(obj); // Line 5
} // Line 9
private void foo(Object param) { // Line 6
String str = param.toString(); //// Line 7
System.out.println(str);
} // Line 8
}
以下の画像は、上記のプログラムに対するスタックメモリとヒープメモリの使用方法、およびプリミティブ、オブジェクト、および参照変数の保存方法を示しています。プログラムの実行手順を確認しましょう。
- プログラムを実行すると、すべてのランタイムクラスがヒープ領域にロードされます。1行目でmain()メソッドが見つかると、Javaランタイムはmain()メソッドスレッドで使用するためのスタックメモリを作成します。
- 2行目でプリミティブなローカル変数を作成していますので、それはmain()メソッドのスタックメモリに作成され、保存されます。
- 3行目でオブジェクトを作成しているため、それはヒープメモリに作成され、スタックメモリにはその参照が含まれます。同様のプロセスが4行目でMemoryオブジェクトを作成する際にも発生します。
- そして、5行目でfoo()メソッドを呼び出すと、foo()メソッドで使用されるスタックのトップにブロックが作成されます。Javaは値渡しであるため、6行目でfoo()スタックブロック内にオブジェクトへの新しい参照が作成されます。
- A string is created in the 7th line, it goes in the String Pool in the heap space and a reference is created in the foo() stack space for it.
- foo()メソッドは8行目で終了し、この時点でfoo()に割り当てられたメモリブロックは解放されます。
- 9行目でmain()メソッドが終了し、main()メソッドのために作成されたスタックメモリも破棄されます。また、この行でプログラムが終了するため、Javaランタイムがすべてのメモリを解放し、プログラムの実行を終了します。
Javaヒープスペースとスタックメモリの違い
上記の説明に基づいて、ヒープとスタックメモリの以下の違いを簡単にまとめることができます。
- ヒープメモリはアプリケーションのすべての部分で使用されますが、スタックメモリは実行スレッドのみで使用されます。
- オブジェクトが作成されると、常にそれはヒープ領域に格納され、スタックメモリにはそれへの参照が含まれます。スタックメモリにはローカルなプリミティブ変数とヒープ領域のオブジェクトへの参照変数のみが含まれます。
- ヒープに格納されたオブジェクトはグローバルにアクセス可能ですが、スタックメモリには他のスレッドからアクセスできません。
- スタックのメモリ管理はLIFOの方法で行われますが、ヒープメモリではより複雑です。なぜなら、それがグローバルに使用されるからです。ヒープメモリはYoung-Generation、Old-Generationなどに分割されます。詳細はJava Garbage Collectionを参照してください。
- スタックメモリは寿命が短いのに対し、ヒープメモリはアプリケーションの実行の開始から終了まで存続します。
- スタートアップサイズとヒープメモリの最大サイズを定義するために-Xmsおよび-Xmx JVMオプションを使用できます。スタックメモリのサイズを定義するために-Xssを使用できます。
- スタックメモリがいっぱいになると、Javaランタイムは
java.lang.StackOverFlowError
をスローしますが、ヒープメモリがいっぱいになると、java.lang.OutOfMemoryError: Java Heap Space
エラーがスローされます。 - スタックメモリのサイズはヒープメモリと比較して非常に少ないです。メモリの割り当てが簡単(LIFO)なため、スタックメモリはヒープメモリと比較して非常に高速です。
これでJava Heap Space vs Stack Memoryに関するJavaアプリケーションの説明は終わりです。Javaプログラムが実行される際のメモリ割り当てに関する疑問が解消されることを願っています。
Source:
https://www.digitalocean.com/community/tutorials/java-heap-space-vs-stack-memory