メソッドエリアの進化の詳細とガベージコレクション
メソッドエリアの進化の詳細
恒久的な世代の進化過程:
- まず第一に、それは明らかです:ホットスポットだけが永続的な世代を持っています。BEA JRockit、IBMJ9などの場合、永続的な生成の概念はありません。原則として、メソッド領域の実装方法は、「Java仮想マシン仕様」に準拠せず、統一性を必要としない仮想マシンの実装詳細に属します。
- ホットスポットのメソッド領域の変更点は次のとおりです
。JDK6:メソッド領域は、JVM仮想マシンメモリ(仮想メモリ)を使用して、永続的な生成によって実装されます。
JDK7:メソッド領域は永続世代によって実装され、JVM仮想マシンメモリを使用します。
JDK8:メソッド領域は、物理マシンのローカルメモリを使用してメタスペースによって実現されます。
永続的な世代をメタスペースに置き換える必要があるのはなぜですか?
公式文書:http://openjdk.java.net/jeps/122
- Java 8の登場により、HotSpotVMに永続的な世代はなくなります。ただし、これは、クラスのメタデータ情報も消えたことを意味するものではありません。これらのデータは、ヒープに接続されていないローカルメモリ領域に移動されます。この領域はメタスペース(メタスペース)と呼ばれます。
- クラスのメタデータはローカルメモリに割り当てられるため、メタスペースの割り当て可能な最大スペースは、システムで使用可能なメモリスペースです。
- この変更は、次の理由で必要です。
-
恒久的な世代のためのスペースのサイズを決定することは困難です。一部のシナリオでは、動的にロードされるクラスが多すぎると、Perm領域でOOMが発生する可能性があります。たとえば、実際のWebプロジェクトでは、ファンクションポイントが多数あるため、実行中のプロセス中に多くのクラスを動的にロードする必要があり、致命的なエラーが頻繁に発生します。例:スレッド「dubboclientxxconnector」の例外java.lang.OutOfMemoryError:PermGenスペース。メタスペースと永続生成の最大の違いは、メタスペースが仮想マシンになく、ローカルメモリを使用することです。したがって、デフォルトでは、メタスペースのサイズはローカルメモリによってのみ制限されます。
-
永続的な世代の調整は非常に困難です。メソッド領域のガベージコレクションは、主に定数プール内の破棄された定数と使用されなくなった型の2つの部分をリサイクルします。メソッド領域は、主にフルGCを減らすために調整されます。
文字列定数プール
文字列定数プールStringTableを調整する必要があるのはなぜですか?
- JDK7では、StringTableはヒープスペースに配置されます。パーマネント世代のガベージコレクションは非常に低いため、パーマネント世代のガベージコレクションはフルGC中に実行され、フルGCは、古い世代のスペースが不足し、パーマネント世代が不十分な場合にのみトリガーされます。 ;
- これにより、StringTableのリサイクル効率は高くなく、開発中に多数の文字列が作成され、リサイクル効率が低くなり、永続的な生成メモリが不十分になり、ヒープに入れて、時間内にメモリを再利用できます。
静的変数を配置する場所
オブジェクトエンティティはどこにありますか?
/**
* 结论:
* 1、静态引用对应的对象实体(也就是这个new byte[1024 * 1024 * 100])始终都存在堆空间,
* 2、只是那个变量(相当于下面的arr变量名)在 JDK6,JDK7,JDK8 存放位置中有所变化
*
* jdk7:
* -Xms200m -Xmx200m -XX:PermSize=300m -XX:MaxPermSize=300m -XX:+PrintGCDetails
* jdk 8:
* -Xms200m -Xmx200m -XX:MetaspaceSize=300m -XX:MaxMetaspaceSize=300m -XX:+PrintGCDetails
*/
public class StaticFieldTest {
private static byte[] arr = new byte[1024 * 1024 * 100];//100MB
public static void main(String[] args) {
System.out.println(StaticFieldTest.arr);
}
}
JDK6環境の場合:
JDK7環境の場合:
JDK8環境の
場合:変数(名前)はどこに格納されますか?
JHSDBツールを使用して以下を分析します。
public class StaticObjTest {
static class Test {
static ObjectHolder staticObj = new ObjectHolder();
ObjectHolder instanceObj = new ObjectHolder();
void foo() {
ObjectHolder localObj = new ObjectHolder();
System.out.println("done");
}
}
private static class ObjectHolder {
}
public static void main(String[] args) {
Test test = new StaticObjTest.Test();
test.foo();
}
}
JDK6環境の場合:
-
staticObjは、Testのタイプ情報とともにメソッド領域に格納されます。
-
instanceObjは、TestのオブジェクトインスタンスとともにJavaヒープに格納されます。
-
localObject(ローカル変数)は、foo()メソッドのスタックフレームのローカル変数テーブルに格納されます。
-
テストの結果、メモリ内の3つのオブジェクトのデータのアドレスはすべて、Eden領域の範囲内にあることがわかりました。したがって、オブジェクトインスタンスがJavaヒープに割り当てられるようにバインドされている限り、結論は次のとおりです。
- 0x00007f32c7800000(エデンエリアの開始アドレス)-0x00007f32c7b50000(エデンエリアの終了アドレス)、
- 3つの変数がこの範囲にあることがわかります。
- したがって、上記の結論を得ることができます。
- 次に、java.lang.ClassのインスタンスにあるstaticObjオブジェクトへの参照を見つけ、このインスタンスのアドレスを指定しました。インスペクターでオブジェクトインスタンスを確認すると、これが実際にjavaであることがはっきりとわかります。タイプlang.Classのオブジェクトインスタンスには、staticobjという名前のインスタンスフィールドがあります。
- 「Java仮想マシン仕様」で定義された概念モデルの観点から、クラス関連の情報はすべてメソッド領域に格納する必要がありますが、「Java仮想マシン仕様」ではメソッド領域の実装方法を指定していません。さまざまな仮想マシンが柔軟に自分自身を制御できるようにするもの。JDK7以降のバージョンのHotSpot仮想マシンは、静的変数とJava言語側のタイプのマップされたClassオブジェクトを一緒に格納し、それらをJavaヒープに格納することを選択します。これも実験から明確に検証されています。
メソッドエリアでのガベージコレクション
-
メソッド領域(ホットスポット仮想マシンでのメタスペースや永続的な生成など)にはガベージコレクションの動作がないと考える人もいますが、そうではありません。「Java仮想マシン仕様」では、メソッド領域の制約が非常に緩く、メソッド領域にガベージコレクションを実装する必要がない場合があるとのことです。実際、メソッド領域タイプのアンロードを実装していないか、完全に実装できなかったコレクターが実際にあります(たとえば、JDK11期間のZGCコレクターはクラスのアンロードをサポートしていません)。
-
一般的に言って、この地域での回復効果、特に荷降ろしの種類を満足させることは困難であり、条件は非常に厳しいです。しかし、地域のこの部分のリサイクルが実際に必要な場合もあります。Sun社の以前のバグリストで発生したいくつかの重大なバグは、HotSpot仮想マシンの低バージョンがこの領域を完全に再利用せず、メモリリークを引き起こしていることが原因です。
-
メソッド領域のガベージコレクションは、主に2つの部分をリサイクルします。定数プール内の破棄された定数と、使用されなくなった型です。
1.メソッド領域の定数プールに主に格納される2つの主要なタイプの定数、リテラルとシンボル参照について説明します。リテラルは、テキスト文字列、finalとして宣言された定数値など、Java言語レベルの定数の概念に比較的近いものです。シンボリック参照は、次の3種類の定数を含む、コンパイルの原則の概念に属します。
- クラスとインターフェースの完全修飾名
- フィールド名と記述子
- メソッド名と記述子
2.定数プール内の定数がどこにも参照されていない限り、定数プールに対するHotSpot仮想マシンのリサイクル戦略は非常に明確です。それらはリサイクルできます。
3.破棄された定数のリサイクルは、Javaヒープ内のオブジェクトのリサイクルと非常によく似ています。(定数の回復に関しては比較的簡単ですが、焦点はクラスの回復にあります)
メソッド領域でのクラスのアンロード:
-
定数が「非推奨」であるかどうかを判断するのは比較的簡単であり、型が「使用されなくなったクラス」に属しているかどうかを判断するための条件はより厳しいものです。次の3つの条件を同時に満たす必要があります。
- このクラスのすべてのインスタンスはリサイクルされています。つまり、このクラスのインスタンスとJavaヒープ内の派生サブクラスはありません。
- クラスをロードしたクラスローダーはリサイクルされています。この条件は、OSGi、JSPリロードなど、慎重に設計された代替クラスローダーシナリオでない限り、通常は達成が困難です。
- このクラスに対応するjava.lang.Classオブジェクトはどこにも参照されておらず、このクラスのメソッドにはリフレクションを介してどこからもアクセスできません。
-
Java仮想マシンは、上記の3つの条件を満たす役に立たないクラスをリサイクルすることが許可されています。ここで説明しているのは「許可」のみであり、オブジェクトと同じではありません。参照がない場合は、再利用されます。タイプをリサイクルするかどうかに関して、HotSpot仮想マシンは制御する-Xnoclassgcパラメーターを提供します。-verbose:classおよび-XX:+ TraceClass-Loading、-XX:+ TraceClassUnLoadingを使用して、クラスのロードおよびアンロード情報を表示することもできます。
-
リフレクション、動的プロキシ、CGLibなどの多数のバイトコードフレームワークを使用してJSPおよびOSGiの頻繁にカスタムクラスローダーを動的に生成するシナリオでは、Java仮想マシンは通常、タイプをアンロードして次のことを保証する機能を備えている必要があります。メソッドは影響を受けません。この領域は過度のメモリプレッシャーを引き起こします。
実行時データ域の要約
面接のよくある質問
- Baidu
- 3つの側面:JVMメモリモデルについて教えてください。領域は何ですか。彼らは何をしていますか?
- Ant Financial:
- Java8のメモリ生成の改善
- JVMメモリはどの領域に分割され、各領域の機能は何ですか?
- 一方:JVMメモリ分散/メモリ構造?スタックとヒープの違いは?ヒープの構造は?なぜ2つのサバイバーエリア?
- 2つの側面:エデンと生存者の比例分布。
- Xiaomi:
- JVMメモリパーティション、なぜ新世代と旧世代が必要なのですか?
- バイトバウンス:
- 両面:Javaのメモリパーティション
- 2番目の側面:vmランタイムデータベース領域について話します。
- 対象はいつ老年期に入りますか?
- Jingdong:
- JVMメモリ構造、エデンとサバイバーの比率。
- JVMメモリを新世代、旧世代、および永続世代に分割する必要があるのはなぜですか。新世代がエデンとサバイバーに分かれているのはなぜですか。
- Tmall:
- 一方:JVMメモリモデルとパーティション。各領域に何を配置するかを詳しく説明する必要があります。
- 一方:Java 8はJVMメモリモデルにどのような変更を加えましたか?
- 拼多多多発:
- JVMメモリはどの領域に分割され、各領域の機能は何ですか?
- 美団:
- Javaメモリ割り当て
- ガベージコレクションは、jvmの永続的な生成で発生しますか?
- 一方:JVMメモリパーティション、なぜ新世代と旧世代が必要なのですか?