JVM メモリ モデルの概要と実践的な演習。私自身も実際に試してみましたが、学んだ後は髪の毛が抜けなくなりました!

JVM メモリ モデルの概要と実践的な演習。私自身も実際に試してみましたが、学んだ後は髪の毛が抜けなくなりました!

[[374276]]

この記事はWeChatの公開アカウント「bugstack」から転載したもので、著者はXiao Fu Geです。この記事を転載する場合は、bugstack の公開アカウントにご連絡ください。

目次

  • 1. はじめに
  • 2. 面接の質問
  • 3. JDK1.6、JDK1.7、JDK1.8 におけるメモリ モデルの進化
  • 4. メモリモデルの各領域の紹介
    • 1. プログラムカウンタ
    • 2. Java仮想マシンスタック
    • 3. ネイティブメソッドスタック
    • 4. ヒープとメタスペース
    • 5. コンスタントプール
  • 5. 手動仮想マシン(メモリモデル)
    • 1. プロジェクト構造
    • 2. キーコード
  • 6. jconsole によるメタスペースオーバーフローの監視
    • 1. 大きなオブジェクトを継続的に作成するコードを見つける
    • 2. メタスペースのサイズを調整する
    • 3. 監視パラメータを設定する
    • 4. テスト実行
  • 七。結論
  • 8. シリーズのおすすめ

1. はじめに

30 歳になるのがどれだけ大変かについての記事を読みました。

各記事の冒頭では、私はいつも世界に対する私の個人的な見解について書きたいと思っています。

最近、30歳になるのがいかに大変かについての記事を見ました。この記事の主人公の中には、勉強、仕事、生活、恋愛などで不幸になっている人もいるようです。これを見逃すか、間違った方向に進んでしまうかのどちらかです。要するに、不運な人たちが人生と戦っているようなものです!

しかし、実際には、遅かれ早かれ、誰もが人生で最も困難な時期に遭遇するかもしれません。私が卒業して間もなく起こった一連の出来事のように;冬に初めて携帯電話を紛失し、水に浸かったパソコンを修理しなければならなくなり、賃貸住宅で初めて騙されました。一連の出来事の中で初めて、給料が支払われる前に朝食や昼食を少なくして、その食事を乗り切れるかどうか試さなければなりませんでした。

ハハハハ、今考えるとなかなか面白いですが、こうしたひどい出来事の多くは、私の認識と能力が不十分だったときに間違った選択をしたために起こったことなのです。

車を運転したいなら、運転免許証を取得する必要があります。遠くまで行きたいなら、能力が必要です。認知力を高め、視野を広げましょう!人生の意味は常に自分自身をアップデートすることです!

2. 面接の質問

飛行機をありがとう、シャオジ!冬の風が吹き、戦いの太鼓が鳴り響く。ベッドの中で、誰が誰を恐れているのでしょうか?

「謝非機」:ねえ?お兄ちゃん、そこにいますか?

インタビュアー:どうしたんですか?週末です。どうしてこんなに早く電話するんですか?

「謝飛機」:私は Google に行って、御社が使うための JVM を書く夢を見て、飛び上がってバグを修正しました。

インタビュアー:えっ!?さて、お聞きしたいのですが、JDK 1.8 と JDK 1.7 の間でランタイム データ領域の設計をどのように最適化しましたか?

「フライトありがとう」: これは私が書いたものではないので、わかりません!

インタビュアー:くそ。 。 。

3. JDK1.6、JDK1.7、JDK1.8 におけるメモリ モデルの進化

図 25-1 JDK1.6、JDK1.7、JDK1.8 のメモリ モデルの進化

図 25-1 は、JDK 1.6、1.7、および 1.8 のメモリ モデルの進化を示しています。実際、このメモリ モデルは、JVM 仮想マシン仕様に準拠した JVM ランタイム データ領域の特定の実装プロセスです。

図 25-1 の各バージョンの反復はすべて、CPU パフォーマンスの向上に適応し、JVM の動作効率を最大化するように設計されています。 JVM メモリ モデルのこれらのバージョン間の主な違いは次のとおりです。

  • JDK 1.6: 永続的な世代があり、静的変数は永続的な世代に保存されます。
  • JDK 1.7: 永続的な世代がありますが、文字列定数プールと静的変数はヒープ上に保存されます。永久世代の使用を徐々に減らします。
  • JDK 1.8: 永続的な世代はありません。ランタイム定数プールとクラス定数プールはすべて、メタスペースと呼ばれることが多いメタデータ領域に格納されます。しかし、文字列定数プールは依然としてヒープ上に格納されています。

4. メモリモデルの各領域の紹介

1. プログラムカウンタ

より小さなメモリ空間 (スレッド専用) には、現在のスレッドによって実行されたバイトコードの行番号が記録されます。

Java メソッドが実行されると、カウンターは仮想マシン バイトコードの現在の命令のアドレスを記録し、ネイティブ メソッドの場合は空になります。

この領域には OutOfMemoryError が定義されていません。

上記はプログラムカウンタの定義です。これが意味不明な場合は、例を挙げてみましょう。

円の円周を計算する Java メソッド コードを定義します。

  1. 公共 静的 フロート円周(フロートr){
  2. 浮動小数点数π = 3.14f;
  3. 浮動小数点領域 = 2 * pi * r;
  4. 返却エリア;
  5. }

次に、図 25-2 は、仮想マシンでのこのコードの実行プロセスを示しており、左側の行番号はプログラム カウンタに対応しています。

図25-2 プログラムカウンタ

これらの各行番号は、スタックのプッシュやポップ、あるいは計算の実行など、実行する必要があるバイトコード命令に対応しています。

スレッドプライベートである理由は、プライベートでない場合、計算プロセス全体の最終結果が間違ってしまうためです。

2. Java仮想マシンスタック

  • 各メソッドが実行されると、ローカル変数テーブル、オペランド スタック、ダイナミック リンク、メソッド終了、スレッドなどの情報を格納するスタック フレームが作成されます。
  • メソッドの呼び出しから実行完了までのプロセスは、スタック フレームが仮想マシンにプッシュされ、仮想マシンからポップされるプロセスに対応します。
  • 最終的には、メソッドが作成され完了するとスタック フレームが破棄されます。

この定義だけではまだあまり意味が分からないかもしれませんので、別の例を見てみましょう。

これはフィボナッチ数列を評価する例です。仮想マシン内でのフィボナッチ数列の実行プロセスを通じて、Java 仮想マシン スタックの目的を理解することができます。

フィボナッチ数列は黄金比数列としても知られ、数学者レオナルド・ダ・フィボナッチがウサギの繁殖の例を用いて導入したことから「ウサギ数列」とも呼ばれています。これは、1、1、2、3、5、8、13、21、34、... という数字の列を指します。数学では、フィボナッチ数列は次のように再帰的に定義されます。F(1)=1、F(2)=1、F(n)=F(n-1)+F(n-2)(n>=3、n∈N*) フィボナッチ数列は、現代物理学、準結晶構造、化学などの分野に直接応用されています。このため、アメリカ数学会は1963年から「フィボナッチ・クォータリー」という数学雑誌を発行しており、この分野の研究成果を発表しています。

図25-3 仮想マシンスタックにおけるフィボナッチ数列の実行プロセス

全体のプロセスはメソッドの呼び出しと戻りです。オペランド スタックの深さとローカル変数のサイズは、呼び出しプロセス中に要求されます。

そして、それぞれの領域から対応する情報を取得して操作するのですが、これが実は積み重ねとポップのプロセスなのです。

3. ネイティブメソッドスタック

ローカル メソッド スタックは、Java 仮想マシン スタックと同様の機能を持ちます。唯一の違いは、ローカル メソッド スタックはネイティブ メソッドを実行するのに対し、仮想マシン スタックは JVM に Java メソッドの実行を提供するという点です。

さらに、Java 仮想マシン スタックと同様に、ネイティブ メソッド スタックでも StackOverflowError および OutOfMemoryError 例外がスローされる可能性があります。

JDK1.8 HotSpot 仮想マシンは、ローカル メソッド スタックと仮想マシン スタックを 1 つに直接結合します。

ローカル メソッド スタックについては上記の例ですでに説明されているため、ここでは詳細には説明しません。

4. ヒープとメタスペース

図25-4 Javaヒープ領域の分割

  • JDK 1.8 JVM のメモリ構造は、主にヒープ メモリ、メタスペース、スタックの 3 つの主要ブロックで構成されます。 Java ヒープはメモリ空間の最大の領域です。
  • Java ヒープは、それぞれ 1/3 と 2/3 を占める若い世代と古い世代で構成されます。
  • 若い世代は、「エデン」、「サバイバーから」、「サバイバーへ」の 3 つの部分に分かれており、比率は 8:1:1 で調整可能です。
  • さらに、ここでは直接メモリ領域であるメタスペースを特別に描画しました。 JDK 1.8 以降では、メソッド領域はヒープ上に割り当てられなくなりました。
  • 「メタスペース」は仮想マシンの Java ヒープからローカル メモリに転送されます。デフォルトでは、メタスペースのサイズはローカル メモリによってのみ制限されます。簡単に言えば、永続的な世代スペースが不足しているために OOM 例外がスローされることはありません。 JDK 1.8 より前のバージョンのクラスおよび JAR パッケージ データは PermGen に保存されます。 PermGen のサイズは固定されており、パブリック クラスはプロジェクト間で共有できないため、OOM 例外が発生する可能性が高くなります。
  • JDK 1.8 にアップグレードすると、メタスペース構成パラメータは -XX:MetaspaceSize=512M XX:MaxMetaspaceSize=1024M になります。次のように、jps と jinfo を通じてメタスペースを表示するためのちょっとしたコツを教えます。

コマンドでメタスペースを表示する

jinfo でデフォルトの MetaspaceSize サイズ (約 20M) を確認すると、MaxMetaspaceSize が比較的大きいことがわかります。

「その他:JDK1.8 メタスペースの紹介:」 Hotspot の永続世代の内容の一部を Java ヒープに移動し、残りをネイティブ メモリに移動します。 http://openjdk.java.net/jeps/122

5. コンスタントプール

JDK 1.7 以降、定数プールは永続世代から分離され、JDK 1.8 では永続世代が削除されました。文字列定数プールは、文字列オブジェクトまたは文字列オブジェクトへの参照を格納するために常にヒープ領域に配置されます。

5. 手動仮想マシン(メモリモデル)

実際、上記の内容では、JVM 仮想マシンのメモリ モデル、つまりランタイム データ領域の構造が十分に紹介されました。しかし、自分で操作できるコードがないので、読んだ後にこのことを忘れてしまうかもしれません。

「それで」、ここでは、Java コードを使用して、データ スロット、スタック フレーム、ローカル変数、仮想マシン スタック、ヒープに関するコード構造のセクションを記述し、仮想マシン メモリ モデルについての印象をより深めたいと思います。

1. プロジェクト構造

ランタイムデータ領域

  1. ランタイムデータ領域
  2. ├── ヒープ
  3. │ ├── コンスタントプール
  4. │ ├── メソッドエリア
  5. │ │ ├── クラス.java
  6. │ │ §── ClassMember.java
  7. │ │ ├── フィールド.java
  8. │ │ ├── メソッド.java
  9. │ │ §── MethodDescriptor.java
  10. │ │ §── MethodDescriptorParser.java
  11. │ │ §── MethodLookup.java
  12. │ │ ├── オブジェクト.java
  13. │ │ ├── スロット.java
  14. │ │ └─ StringPool.java
  15. │ └── ClassLoader.java
  16. ├── フレーム.java
  17. ├── JvmStack.java
  18. ├── LocalVars.java
  19. ├── オペランドスタック.java
  20. ├── スロット.java
  21. └── スレッド.java

上記の部分は、Java を使用して実装された JVM 仮想マシン機能の一部であり、主に次の内容が含まれます。

  • フレーム、スタックフレーム
  • JvmStack、仮想マシンスタック
  • LocalVars、ローカル変数
  • OperandStack、オペランドスタック
  • スロット、データスロット
  • 定数プールとメソッド領域を含むヒープ

2. キーコード

オペランドスタック

  1. パブリッククラスOperandStack {
  2.  
  3. プライベートint  サイズ= 0;
  4. プライベートSlot[] スロット;
  5.  
  6. パブリックオペランドスタック( int maxStack) {
  7. (最大スタック>0)の場合{
  8. スロット = 新しいスロット[maxStack];
  9. ( int i = 0; i < maxStack; i++) {
  10. スロット[i] = 新しいスロット();
  11. }
  12. }
  13. }
  14. //...
  15. }

「仮想マシンスタック オペランドスタック」

  1. パブリッククラス JvmStack {
  2.  
  3. プライベートint maxSize;
  4. プライベートint  サイズ;
  5. プライベートフレーム_top;
  6.      
  7. //...
  8. }

「スタックフレーム」

  1. パブリッククラスフレーム{
  2.  
  3. //スタックはリンクリストとして実装されます
  4. フレーム下部;
  5.  
  6. //ローカル変数テーブル
  7. プライベート LocalVars localVars;
  8.  
  9. //オペランドスタック
  10. プライベートオペランドスタック オペランドスタック;
  11.  
  12. プライベートスレッド スレッド;
  13.  
  14. プライベートメソッドメソッド;
  15.  
  16. プライベートint nextPC;
  17.   
  18. //...
  19. }
  • コード構造について何かご存知ですか?
  • スロット データ スロットは、データを格納するために使用される配列構造です。
  • オペランド スタックとローカル変数テーブルはどちらも、スタック操作にデータ スロットを使用します。
  • スタック フレームでは、接続、ローカル変数テーブル、オペランド スタック、メソッド、スレッドなどを確認できます。そして、記事にあるように、新しいメソッドが実行されると、スタック フレームが作成されます。そうですか?今なら本当に理解できます。
  • JVM の実装に興味がある場合は、Java で JVM を実装するソース コードを読むことができます: https://github.com/fuzhengwei/itstack-demo-jvm

6. jconsole によるメタスペースオーバーフローの監視

JDK 1.8 のメモリ モデルでは、永続世代が削除され、メタスペースに置き換えられたのではないですか?しかし、テストしなければ、感じることができず、証拠もありません!

コード ロジックに関するすべての学習には、深い印象を残すためのデータ基盤と証明プロセスが必要です。さあ、メタスペースを埋め尽くして OOM にしましょう!

1. 大きなオブジェクトを継続的に作成するコードを見つける

  1. 公共 静的void main(String[] args)はInterruptedExceptionをスローします{
  2.      
  3. スレッドをスリープ状態にします(5000);
  4.      
  5. クラスロードMXBeanのloadingBean = ManagementFactory.getClassLoadingMXBean();
  6. )の間{
  7. エンハンサー enhancer = new Enhancer();
  8. エンハンサー.setSuperclass(MetaSpaceOomMock.class);
  9. enhancer.setCallbackTypes(新しい Class[]{Dispatcher.class、MethodInterceptor.class});
  10. エンハンサー.setCallbackFilter(新しいCallbackFilter() {
  11. @オーバーライド
  12. 公共  int accept(メソッドメソッド) {
  13. 1 を返します
  14. }
  15. @オーバーライド
  16. パブリックブール値が等しい(オブジェクトobj) {
  17. super.equals(obj)を返します
  18. }
  19. });
  20. システム。出力.println(enhancer.createClass().getName() + loadingBean.getTotalLoadedClassCount() + loadingBean.getLoadedClassCount() + loadingBean.getUnloadedClassCount());
  21. }
  22. }

インターネット上で CGLIB に基づいたセクションを見つけました。他にも書いてみてください。

Thread.sleep(5000); しばらくスリープして確認できるようにします。そうしないと、プログラムの実行速度が速すぎると異常になります。

2. メタスペースのサイズを調整する

デフォルトでは、メタスペースは結果をテストするには大きすぎるため、小さいサイズに調整します。

  1. -XX:メタスペースサイズ=8m
  2. -XX:最大メタスペースサイズ=80m

3. 監視パラメータを設定する

jconsole 監視に基づいて、次のパラメータを設定する必要があります。

  1. -Djava.rmi.server.ホスト名=127.0.0.1
  2. -Dcom.sun.management.jmxremote
  3. -Dcom.sun.management.jmxremote.port=7397
  4. -Dcom.sun.management.jmxremote.ssl= 
  5. -Dcom.sun.management.jmxremote.authenticate= 

4. テスト実行

4.1 構成パラメータ

上記のテスト パラメータは、次のように IDEA で設定してプログラムを実行できます。

図25-5 プログラム実行パラメータの設定とOOMの監視

また、jconsole は IDEA が提供するターミナルから起動することもできます。 jconsole と入力して Enter キーを押すだけです。

4.2 テスト結果

  1. org.itstack.interview.MetaSpaceOomMock$$EnhancerByCGLIB$$bd2bb16e999099900
  2. org.itstack.interview.MetaSpaceOomMock$$EnhancerByCGLIB$$9c774e64999199910
  3. org.itstack.interview.MetaSpaceOomMock$$EnhancerByCGLIB$$cac97732999299920
  4. org.itstack.interview.MetaSpaceOomMock$$EnhancerByCGLIB$$91c6a15a999399930
  5. スレッド「main」例外が発生しましjava.lang.IllegalStateException:  キャッシュアイテムを読み込む
  6. net.sf.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:79)
  7. net.sf.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)
  8. net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:119)
  9. net.sf.cglib.core.AbstractClassGenerator作成(AbstractClassGenerator.java:294)
  10. net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)
  11. net.sf.cglib.proxy.Enhancer.createClass(Enhancer.java:337)
  12. org.itstack.interview.MetaSpaceOomMock.main(MetaSpaceOomMock.java:34)
  13. 原因: java.lang.OutOfMemoryError: Metaspace
  14. java.lang.Class.forName0 (ネイティブメソッド)
  15. java.lang.Class.forName(Class.java:348)
  16. net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:467)
  17. net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:339)
  18. net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)
  19. net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:96)
  20. net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:94)
  21. net.sf.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54)
  22. java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
  23. java.util.concurrent.FutureTask.run(FutureTask.java)
  24. net.sf.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61)
  25. ... 6件以上

これが私たちが求めている文、「java.lang.OutOfMemoryError: Metaspace, metaspace OOM」です。これは、JDK1.8 で permanent 世代が削除され、metaspace に置き換えられたことを証明しています。

4.3 スクリーンショットの監視

図25-6 jconsoleによるメタスペースオーバーフローの監視

図25-6は、監視プログラムのOOMが発生した場合のメタスペース表現を示しています。これで、このメタスペースの雰囲気がつかめました。

七。結論

この記事では、プログラム カウンター、Java 仮想マシン スタック、ネイティブ メソッド スタック、ヒープ、メタスペースなど、さまざまな領域を理解するために、さまざまなバージョンの JDK におけるメモリ モデル構造の進化について説明します。また、JDK 1.8 以降でメソッド領域を削除し、メタスペースを導入する主な目的と機能を理解します。

JVM コードを手動で読み取ることで、誰もがランタイム データ領域を包括的に理解できるようになり、この方法により、誰もがこの部分の知識を習得できるようになります。

最後に、jconsole を使用して Metaspace オーバーフローのプロセス全体を検出し、学習した内容を実践して、Metaspace が解決する問題とそれをテストする方法を確認します。

<<:  企業に利益をもたらすクラウドコンピューティングの利点とは

>>:  DevOps と DataOps のために Kubernetes に移行するメリットは何ですか?

推薦する

マイクロソフトは自社のマーケティングの弱点を浮き彫りにするためにライバルを嘲笑している

Appleは6月からさらに脱Google化を進め、Siri音声アシスタントが提供する検索エンジンサー...

Hongmeng HarmonyOS 開発中の分散フロー開発における一般的なエラーに関する FAQ

[[385509]]詳細については、以下をご覧ください。 51CTOとHuaweiが共同で構築したH...

Sina WeiboとLakalaの提携疑惑からマーケティングについて学ぶ

本日、Sina が Lakala と O2O 分野およびモバイル決済で提携した場合、両社が提携する可...

インターネット時代:言論の復活

文/ 辛海光当初私は、ドラゴンを倒すことができるスキルであるスピーチは、中国のような国では将来がない...

timeweb: 老舗ロシアVPS業者、中国語ページとWeChat決済を追加、月額5元

ロシアの業者であるtimewebは13年以上運営されており、今でも非常に有名で、ロシアのVPS業界で...

justg: 500M 帯域幅「3 つのネットワーク」 - 「南アフリカ cn2 gia vps」簡単な評価

justg は以前、100M 帯域幅の VPS を宣伝していました。そのときの特別価格は年間 19....

WeChatプロモーションスキル | WeChatパブリックアカウントを運用するための実践的な戦略!

見出しの頭痛、いつまでたってもうまくいかない話題、次から次へと流れていくホットな話題、人気記事の閲覧...

ZooKeeper 分散ロック キュレーター ソース コード 1: 再入可能ロック

序文一般的な作業でよく使用される分散ロックは、Redis と ZooKeeper に基づいています。...

従来のビジネスをパブリック クラウドに移行する際の落とし穴を回避するためのガイド

[[396459]]この記事はWeChatの公開アカウント「New Titanium Cloud S...

ランキングもトラフィックもないウェブサイトの未来はどこにあるのでしょうか? - A5 Webmaster Network

最近、地元の自動車情報サイトが開設されてから6ヶ月以上経ち、百度、捜狗、360などのサイトに掲載され...

A5フォーラムの重要性がいかに高いかを証明する例

A5フォーラムは、ウェブマスターネットワークのウェブマスター交流フォーラムです。A5フォーラムは20...

Baiduスパイダーを刺激して、含まれているがランク付けされていないという問題を解消する

長い間記事を書いていませんでした。最近とても忙しかったです。百度の頻繁な更新は、主要な草の根ウェブマ...

インターネットは部族主義に戻り、「コミュニティ+」は広告主がユーザーに直接支払うことを可能にする

モバイルアプリケーション業界のデータ調査機関であるTalkingDateの5月の統計報告によると、国...

sharktech - 99 ドル / オランダ データ センター / E3-1230V2 / 16g メモリ / 2T ハード ドライブ / 29IP / 40gDDos 防御

Sharktech は、オランダのアムステルダム データ センターに設置され、デフォルトで 40Gb...

コンピュータ専門家がフィッシングサイトを作成したが、これは百度でシノペックの公式ウェブサイトよりも上位にランクされている。

17歳のシャオ・リンは小学校しか卒業していないが、興味があったため独学でコンピューターのウェブサイト...