ランタイムデータ領域-ヒープ
ヒープのコアコンセプト
「Java仮想マシン仕様」でのJavaヒープの説明は、次のとおりです。すべてのオブジェクトインスタンスと配列は、実行時にヒープに割り当てる必要があります。(ヒープは、すべてのクラスインスタンスと配列のメモリが割り当てられるランタイムデータ領域です)。
しかし、実用的な観点からは、「ほぼ」すべてのオブジェクトインスタンスがここにメモリを割り当てます。スタックにはまだいくつかのオブジェクトが割り当てられており、配列とオブジェクトがスタックに格納されない可能性があるため、スタックフレームは参照を保存するため、この参照はヒープ内のオブジェクトまたは配列の場所を指します。
- ヒープはJVMプロセスに固有です。つまり、プロセスはJVMインスタンスに対応しますが、プロセスには複数のスレッドが含まれ、それらは同じヒープスペースを共有します。
- JVMインスタンスのヒープメモリは1つだけであり、ヒープはJavaメモリ管理のコア領域です。
- Javaヒープ領域は、JVMの起動時に作成され、そのサイズが決定されます。これは、JVMによって管理される最大のメモリ空間です。
- ただし、ヒープメモリのサイズは調整できます。
- 「Java仮想マシン仕様」では、ヒープは物理的に不連続なメモリスペースに配置できると規定されていますが、論理的には連続していると見なす必要があります(オペレーティングシステムと比較して)。
- Javaヒープは、スレッド専用バッファー(Thread Local Allocation Buffer、TLAB)に分割できます。これは、ヒープ内のすべての情報がスレッドによって共有されるわけではないことを意味します。
次のコードは、ヒープメモリを説明しています。
public class HeapDemo {
public static void main(String[] args) {
System.out.println("start...");
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end...");
}
}
ヒープサイズを設定します。
-Xms10m:最小ヒープメモリ
-Xmx10m:最大ヒープメモリ
次の図は、Java VisualVMを使用して、VisualVMのGCプラグインを介してヒープスペースの内容を表示する方法を示しています。
メソッドが終了した後、ヒープ内のオブジェクトはすぐには削除されません。ガベージコレクション中にのみ削除されます。つまり、GCがトリガーされたときに、オブジェクトが収集されます。
ヒープ内のオブジェクトがすぐに再利用されると、ユーザースレッドが影響を受けるためです。
ヒープ、Javaスタック、およびメソッド領域間の接続:
ヒープメモリの内訳
Java 7以前のヒープメモリは、論理的に3つの部分に分けられます。新生児領域+高齢者介護領域+永続領域:
- ヤングジェネレーションスペースヤング/ニューはエデンエリアとサバイバーエリアに分かれています。
- テニュア生成スペース旧/テニュア;
- パーマネントスペースパーマ。
Java 8以降のヒープメモリは、論理的に3つの部分に分けられます。新生児エリア年金エリア+メタスペース:
- ヤングジェネレーションスペースのヤング/ニューは、エデンエリアとサバイバーエリアに分かれています。
- テニュア生成スペース旧/テニュア;
- メタスペースメタ。
コンベンション:新生児エリア->新世代->若い世代、シニアケアエリア->古いエリア->古い世代、パーマネントエリア->パーマネント世代
JDK1.8より前のヒープスペースの内部構造は、パーマネント世代をメタスペース!!!
ヒープスペースには、論理的に新生代、旧世代、メタスペースが含まれますが、実際には新生代と旧世代のみが含まれます。メタスペースはメソッド領域とも呼ばれます。!!
ヒープメモリサイズとOOMを設定する
Javaヒープ領域はJavaオブジェクトインスタンスを格納するために使用されるため、ヒープサイズはJVMの起動時にすでに設定されており、オプション「-Xmx」および「-Xms」で設定できます。
- 「-Xms」は、ヒープ領域の初期メモリを示すために使用されます。これは、-xx:InitialHeapSizeと同等です。
- 「-Xmx」は、ヒープ領域の最大メモリを示すために使用されます。これは、-XX:MaxHeapSizeと同等です。
- ヒープ領域のメモリサイズが「-xmx」で指定された最大メモリを超えると、outofMemoryError例外がスローされます。
- 通常、2つのパラメーター-Xmsと-Xmxは同じ値で構成されます。目的は、Javaガベージコレクションメカニズムがヒープ領域をクリーンアップした後、ヒープ領域を再計算せずにヒープ領域のサイズを計算できるようにすることです。これにより、パフォーマンスが向上します。
デフォルト:
- 初期メモリサイズ:物理コンピュータのメモリサイズ/ 64;
- 最大メモリサイズ:物理コンピュータのメモリサイズ/ 4。
ヒープメモリサイズを表示するための次のコードテスト:
public class HeapSpaceInitial {
public static void main(String[] args) {
//返回Java虚拟机中的堆内存总量
long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
//返回Java虚拟机试图使用的最大堆内存量
long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;
System.out.println("-Xms : " + initialMemory + "M");
System.out.println("-Xmx : " + maxMemory + "M");
//System.out.println("系统内存大小为:" + initialMemory * 64.0 / 1024 + "G");
//System.out.println("系统内存大小为:" + maxMemory * 4.0 / 1024 + "G");
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
出力結果
-Xms : 246M
-Xmx : 3934M
ヒープメモリのメモリ割り当てを表示する方法:
jps -> jstat -gc 进程id
OutOfMemoryの例
コード例:
public class OOMTest {
public static void main(String[] args) {
ArrayList<Picture> list = new ArrayList<>();
while(true){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add(new Picture(new Random().nextInt(1024 * 1024)));
}
}
}
class Picture{
private byte[] pixels;
public Picture(int length) {
this.pixels = new byte[length];
}
}
スタートアップパラメータを設定する
-Xms500m -Xmx:500m
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.heu.heap.OOMTest.main(OOMTest.java:21)
VisualVMツールを使用して特定のメモリ使用量を表示します。
使用済みヒープが500に達すると、OOM例外が発生することがわかります。
老いも若きも
JVMに格納されているJavaオブジェクトは、次の2つのカテゴリに分類できます。
- 1つのタイプは、ライフサイクルが短い瞬間的なオブジェクトです。このタイプのオブジェクトは、非常に迅速に作成され、消滅します。ライフサイクルが短い場合は、時間内にリサイクルできます。
- 別のタイプのオブジェクトのライフサイクルは非常に長く、極端な場合でも、JVMのライフサイクルと一致している可能性があります。
Javaヒープ領域をさらに細かく分割すると、若い世代(YoungGen)と古い世代(oldGen)に分けることができます。
その中で、若い世代は、エデン空間、サバイバー0空間、サバイバー1空間(エリアからエリアへと呼ばれることもあります)に分けることができます。
次のヒープパラメータは、通常、開発中に調整されません。
- エデン:From:to-> 8:1:1
- 新世代:旧世代-> 1:2
ヒープ構造内の若い世代と古い世代の比率を構成します。
- デフォルトの-XX:NewRatio = 2は、新しい世代が1を占め、古い世代が2を占め、新しい世代がヒープ全体の3分の1を占めることを意味します。
- -XX:NewRatio = 4を変更できます。これは、新しい世代が1を占め、古い世代が4を占め、新しい世代がヒープ全体の1/5を占めることを意味します。
- プロジェクト全体でライフサイクルの長いオブジェクトが多すぎることがわかった場合は、調整のために古い世代のサイズを調整できます。
- HotSpot仮想マシンでは、Edenスペースと他の2つのサバイバースペースのデフォルトの比率は8:1:1です。もちろん、開発者はオプション「-xx:SurvivorRatio」を使用してこのスペース比率を調整できます。たとえば、-xx:SurvivorRatio = 8です。
ほとんどすべてのJavaオブジェクトはEden領域で新しく、ほとんど(80%)のJavaオブジェクトは新しい世代で破棄されます(一部の大きなオブジェクトは、Eden領域に格納できない場合、古い世代に直接移動します)。
オプション「-Xmn」を使用して、新世代の最大メモリサイズを設定できます。
また、このパラメーターは通常、デフォルト値を使用します。