仮想マシンオブジェクト作成の秘密を1つの記事でマスターする

仮想マシンオブジェクト作成の秘密を1つの記事でマスターする

流砂の上にプラットフォームを構築しないでください。遅かれ早かれ、あなたがしたことの代償を払うことになります。

  • ポジティブな人間になる
  • コーディング、バグ修正、自己改善
  • プログラミングの楽園、春が来ました!
  • [[320720]]

この記事の主な内容では、HotSpot 仮想マシンが Java ヒープ内にオブジェクトを作成し、メモリ レイアウトを割り当て、それらにアクセスする方法について説明します。

この記事の地図:

1. オブジェクトを作成する

シーズン1から観ている方なら、クラスのライフサイクル全体について語る箇所があることはご存知だと思います。これまでは初期化フェーズについてのみ説明しました。クラスの使用方法とアンインストール方法はまだ説明されていません。次に、この記事ではクラスの使用について簡単に紹介します。花や翡翠のように美しい女の子を創りましょう!

ここで復習しましょう。クラスが仮想マシンのメモリにロードされてからメモリからアンロードされるまでのライフ サイクルには、次の 7 つの段階が含まれます。

  • 読み込み中
  • 検証
  • 準備
  • 解決
  • 初期化
  • 使用
  • 荷降ろし

Java では、クラスを使用するときに、そのクラスのインスタンスを作成することが多く、これをオブジェクトの作成と呼びます。実際、Java プログラムの実行中は常にオブジェクトが作成されます。オブジェクトの作成 (クローン作成、デシリアライズなど) は通常、単なる新しいキーワードです。しかし、Java 仮想マシンでオブジェクト (配列や Class オブジェクトなどを含まない通常の Java オブジェクト) を作成するプロセスとは何でしょうか?

まず、仮想マシンが新しい命令に遭遇すると、まずこの命令のパラメータが定数プール内のクラスへのシンボリック参照を見つけることができるかどうかを確認します。次に、このシンボリック参照によって表されるクラスがロードされ、解析され、初期化されているかどうかを確認します。クラスのロードが実行されない場合は、対応するクラスのロード プロセスが実行されます。覚えておいてください: 新しいオブジェクトを作成するには、まずクラスをロードする必要があります。

2 番目: クラスのロード チェックに合格すると、仮想マシンは新しいオブジェクトにメモリを割り当てます。オブジェクトに必要なメモリ サイズは、クラスがロードされたときに完全に決定できます (オブジェクトの決定方法については後述します)。オブジェクトにメモリを割り当てるタスクは、Java ヒープから特定のサイズのメモリ ブロックを割り当てることと同じです。割り当て方法には、「ポインタ衝突」と「フリーリスト」の 2 つがあります。割り当て方法の選択は、Java ヒープが正規かどうかによって決まり、Java ヒープが正規かどうかは、使用するガベージ コレクターに圧縮およびソート機能があるかどうかによって決まります (ヒープ上のオブジェクトの分割は複雑な問題であり、後で説明します。ここでは、メモリがヒープ上に割り当てられることを理解するだけで十分です)。覚えておいてください: 新しいオブジェクトを作成するには、まずメモリ領域を割り当てる必要があります。

3 つ目: メモリの割り当てが完了したら、仮想マシンは割り当てられたメモリ領域をゼロ値に初期化する必要があります (ゼロ値の概念は前回の記事で紹介されているため、ここでは説明しません)。このステップにより、プログラムはこれらのフィールドのデータ型に対応するゼロ値にアクセスできるため、初期値を割り当てずにオブジェクトのインスタンスのフィールドを Java コードで直接使用できるようになります。覚えておいてください: 新しいオブジェクトを作成する場合、仮想マシンはオブジェクトのインスタンスのフィールドにゼロ値を自動的に割り当てます。

4 番目: 仮想マシンは、オブジェクトがどのクラスのインスタンスであるか、クラスのメタデータ情報を見つける方法 (JDK7 はメソッド領域に保存されます)、オブジェクトのハッシュ コード、オブジェクトの GC 生成年齢などの情報など、オブジェクトに必要な設定を行う必要があります。この情報はオブジェクトのオブジェクト ヘッダーに保存されます。

上記の作業が完了すると、仮想マシンの観点からはオブジェクトが作成されたことになります。しかし、Javaプログラムの観点から見ると、オブジェクトの生成はまだ始まったばかりです。メソッドはまだ実行されておらず、すべてのフィールドの値はゼロです。一般的に言えば、新しい命令の後は、それが実行されますメソッドは、プログラマーの希望に応じてオブジェクトを初期化し、実際に使用可能なオブジェクトが完全に生成されます。

覚えておいてください: オブジェクトは、単に作成したいからといって作成できるわけではありません。

以下はグラフィカルな例 (バージョン jdk1.7) を使用した簡単な説明です。

まず最初はPrettyGirlクラス!

  1. パブリッククラスPrettyGirl {
  2. /**
  3. * 女の子の苗字と名前は何ですか?
  4. */
  5. 文字列;
  6. /**
  7. * 年
  8. */
  9. 年齢;
  10. /**
  11. * どこに住んでいますか?
  12. */
  13. 静的文字列アドレス;
  14. /**
  15. * 結婚したことがありますか?
  16. */
  17. ブール値の結婚;
  18. void sayHello(){
  19. システム。 .println ( "こんにちは..." );
  20. }
  21. @オーバーライド
  22. パブリック文字列toString() {
  23. 戻る  「プリティガール{」 +
  24. "名前='" +名前+ '\ '' +
  25. ", 年齢=" + 年齢 +
  26. ", 結婚する=" + 結婚する +
  27. '}' ;
  28. }
  29. }

メソッド領域には、クラス構造の格納に加えて、静的プロパティと静的メソッドも格納されます。中小規模のプログラムを作成する場合、メソッド領域でのメモリオーバーフローは一般的に発生しません。前回の記事でも触れましたが、JDK1.8にはメソッド領域の概念はありません。説明のため、ここでの図はまだ JDK1.7 です。

2番目: 新しい2人の美しい女の子をインスタンス化します!

  1. 公共 静的void main(String[] args) {
  2. PrettyGirl pg1 = 新しい PrettyGirl();
  3. pg1.name = "アリス" ;
  4. 年齢 = 18;
  5. pg1.address = "長沙" ;
  6. PrettyGirl pg2 = 新しい PrettyGirl();
  7. pg2.name = "アレクシア" ;
  8. pg2.年齢 = 28;
  9. システム。出力.println(pg1 + " ---" + pg1.address);
  10. システム。出力.println(pg2 + "----" + pg2.address);
  11. }
  12. ----印刷結果:--------  
  13. PrettyGirl{ name = 'Alice' 、 age=18、 married= false } ---changsha  
  14. PrettyGirl{ name = 'Alexia' , age=28, married= false } ----changsha  

スタック メモリに pg1 変数用のスペースを割り当て、ヒープ メモリに PrettyGirl オブジェクト用のスペースを割り当てます。初期化後、そのアドレス値を pg1 に返し、pg1.name と pg1.age を通じてその値を変更します。静的変数アドレスはクラス内で公開されています。

ヒープは、元のクラスへの参照を維持しながら、オブジェクトが保持するデータを格納します。単純に言えば、オブジェクト属性の値がヒープ領域に格納され、オブジェクトから呼び出されるメソッドがメソッド領域に格納されることになります。

上の図から、スタックと呼ばれる領域があることもわかります。プログラムの実行中にメソッド呼び出しが発生するたびに、Java 仮想マシンはスタック フレーム (スレッド専用で、ヒープ スレッドとメソッド領域スレッドによって共有される) と呼ばれるスタック内のメモリを割り当てます。上記のプログラムのように、main メソッドが呼び出されるとスタックが作成されます。スタック フレーム内のメモリは、ローカル変数 (基本型と参照型を含む) に使用されます。基本型と参照型については後ほど詳しく紹介します。メソッド呼び出しが終了すると、仮想マシンはスタック フレームによって占有されていたメモリを再利用します。

ヒント: レビュー

1. ヒープメモリがオーバーフローすると、OutOfMemoryError エラーが発生し、「Java ヒープ領域」というプロンプトメッセージが表示されます。

2. スタックには 2 つの例外があります。

  • スレッドによって要求されたスタックの深さが仮想マシンで許可される最大深さより大きい場合、StackOverflowError 例外がスローされます (再帰によってこの例外が発生する可能性があります)。
  • スタックを拡張するときに仮想マシンが十分なメモリ領域を適用できない場合、OutOfMemoryError 例外がスローされます。

3. メソッド領域がある場合、「PermGen space」というプロンプトメッセージとともに、OutOfMemoryError エラーも発生します。 (このエラーメッセージはJDK8以降では表示されません)

各エリアには設定できるパラメータがいくつかあり、パラメータ学習は今後も更新されていきます!

2. オブジェクトメモリレイアウト

オブジェクトを作成するのは簡単ではありません。

HotSpot 仮想マシンでは、メモリ内のオブジェクトのレイアウトは、オブジェクト ヘッダー、インスタンス データ、オブジェクト パディングの 3 つの領域に分けられます。

以下は、これら 3 つの領域についての簡単な紹介です。

1. オブジェクトヘッド - まだまだ見た目の時代!

オブジェクト ヘッダーには 2 つの情報部分が含まれます。最初の部分は、オブジェクト自体のランタイムデータを格納するために使用されます。

  • ハッシュ コード (HashCode) は、オブジェクトのハッシュコードが一意であり、オブジェクトがシングルトンであるかどうかを判断するなどです。
  • GC 世代の年齢 (新しい世代か古い世代かを示します)
  • ロック ステータス フラグ、スレッドによって保持されているロック、バイアスされたスレッド ID (マルチスレッドと同期に使用)
  • その他など…

注: より深い理解を得るには、上記の点を他の関連知識と組み合わせて関連付ける必要があります。

たとえば、ハッシュコード hashCode の場合、次の 2 つの質問について何かご意見がありましたら、メッセージを残して議論してください。

  • equals を書き換える場合は、hashcode も書き換える必要があります。考えてみてください、なぜでしょうか? HashMap を書き直さないと、どのような問題が発生しますか?
  • 同じキーを持つデータを置き換えずにオーバーレイして HashMap に保存するにはどうすればよいですか?

質問 2 のヒント: キーの hashCode() メソッドと Map の put() メソッドをオーバーライドする限り、実際には同じキーの下に異なる値を格納できます。

2 番目の部分は型ポインターです。これは、オブジェクトがそのクラス メタデータを指すポインターです。仮想マシンはポインターを使用して、オブジェクトがどのクラスのインスタンスであるかを判断します。 (上の写真の矢印のように、単純にポインターとして理解できます!)

例:

(1) すべての仮想マシン実装がオブジェクトデータの型ポインタを保持する必要はありません。つまり、オブジェクトのメタデータを見つけるために、オブジェクト自体を調べる必要はありません。

(2)オブジェクトがJava配列の場合、オブジェクトヘッダーに配列の長さを記録するデータが存在する必要がある。これは、仮想マシンは通常のJavaオブジェクトのメタデータを通じてJavaオブジェクトのサイズを判断できるが、配列のサイズは配列のメタデータから判断できないためである。

2. サンプルデータ - 外面の美しさを理解した後は、内面の美しさにも注目しましょう。

インスタンス データ部分は、オブジェクトに実際に格納されている有効な情報、つまりプログラム コードで定義されたさまざまな種類のフィールドの内容です。

親クラスから継承されたものでも、サブクラスで定義されたものでも、記録する必要があります。レコードの保存順序は、仮想マシン割り当て戦略パラメータと、Java ソース コードでフィールドが定義されている順序によって影響を受けます。

3. 配置と塗りつぶし - 配置と塗りつぶしがインターネットの標準的な有名人になります。

オブジェクトのパディングは必ずしも存在するわけではなく、特別な意味もありません。プレースホルダーとしてのみ機能します。 HotSpot VM の自動メモリ管理システムでは、オブジェクトの開始アドレスが 8 バイトの整数倍である必要があるため、オブジェクトのサイズは 8 バイトの整数倍である必要があります。オブジェクト ヘッダーは 8 バイトの整数倍なので、オブジェクト インスタンス データが揃っていない場合は、それを補完するために埋める必要があります。

(記憶整合充填と同様に、美的基準により、ハンサムな顔と良い体型を持って生まれた人は、他の充填を必要としません。一部の人は、美しい顔を持っているかもしれませんが、一部の部分は標準に達していないため、標準を満たすために充填する必要があります)

ヒント: バイト

バイトは、コンピューターの記憶領域に使用される基本的な測定単位です。 8 バイナリ ビットが 1 バイトを構成します。つまり、1 バイト = 8 ビットです。

3. 物体の「日付」(位置)を特定する方法

誰かと知り合った後、いつも WeChat でチャットできるわけではなく、食事などをする約束もしなければなりません。 Java でオブジェクトを作成する場合は、そのオブジェクトを使用する必要があります。 Java プログラムはどのようにして特定のオブジェクトを見つけるのでしょうか?

Java プログラムでは、スタック上の参照データを使用してヒープ上の特定のオブジェクトを操作する必要があります (冒頭の図に示すように、スタック上の参照はヒープ内の特定のオブジェクトを指します)。ただし、Java 仮想マシン仕様の Reference 型はオブジェクトへの参照のみを定義し、この参照がヒープ内のオブジェクトの特定の場所をどのように検索してアクセスするかは定義していないため、オブジェクト アクセス メソッドも仮想マシンの実装に依存します。

現在、主流のアクセス方法は、ハンドルと直接ポインターを使用しています。

まず、ハンドル

ハンドル アクセスを使用する場合、メモリの一部が Java でハンドル プールとして割り当てられます。参照にはオブジェクトのハンドル アドレスが格納され、ハンドルには図に示すように、オブジェクトのインスタンス データと型データの特定のアドレス情報が含まれます。

2番目: 直接ポインタ

直接ポインタを使用する場合は、アクセス タイプ配列の関連情報を Java ヒープ オブジェクトのレイアウトに配置する方法を考慮する必要があり、図に示すように、参照にはオブジェクトのアドレスが直接格納されます。

どちらの方法にもそれぞれ利点があり、簡単にまとめると次のようになります。

ハンドル: 最大の利点は、参照によって安定したハンドル アドレスが格納されることです。オブジェクトが移動されると (ガベージ コレクションとオブジェクトの移動は非常に一般的です)、ハンドル内のインスタンス データ ポインターのみが変更され、参照自体は変更する必要はありません。

ダイレクトポインター:最大の利点は速度が速いことです。ポインターの位置決めのオーバーヘッドを節約します。 Java ではオブジェクト アクセスが非常に頻繁に行われるため、この種のオーバーヘッドを削減することはパフォーマンスを向上させる上で非常に重要です。

仮想マシンのホットスポットは直接ポインターを使用します。しかし、他の言語やフレームワークでもハンドルを使用することは一般的です。

IV.結論

この記事では主に、Java でのオブジェクトの作成、オブジェクトのメモリレイアウト、オブジェクトの検索方法についてまとめています。また、オブジェクトは単に新しいものである必要があるというわけではなく、新しいオブジェクトを「登録」する方法はいくつかあることもわかります。

<<:  国が学校を再開し、「早送りボタン」を押す中、オンライン教室は他に何ができるでしょうか?

>>:  管理者が知っておくべき6つの構成管理ツール

推薦する

百度の5つの高圧線について簡単に説明する

Baidu は Baidu Knows に非常に高い重みを与えており、そこに外部リンクを作成するのは...

Amazon Web Servicesが多数の新しいデータベース機能を発表

[51CTO.com クイック翻訳] Amazon の Amazon Web Services (A...

クラウドネイティブ Docker デプロイメント Flask 実践

1. Dockerの理解1. Dockerの簡単な紹介Docker はオープンソースのアプリケーシ...

予算vm-$169/253IP/E3-1270V3/32Gメモリ/2Thdd/240gSSD/20Tトラフィック/Gポート/ロサンゼルス

Enzuの有名なIDCブランドbudgetvmは、ロサンゼルスデータセンターで特別なサーバープロモー...

モンスターメグス - 3.5 USD/10g SSD/無制限のウェブサイト構築/無料ドメイン名/フェニックス/20g DDOS 保護

monstermegs.com は 2010 年に設立されたホスティング会社です。主に仮想ホストを運...

謝文:ビッグデータの時代には、少しの砂で塔が建てられ、少しの毛皮でコートが作られる

地球上のどこで石を拾っても鉄を見つけることができます。しかし、鉄鉱石が世界中どこにでもあると言うのは...

水秋池がシングルページのコピーライティングで使用されるマーケティングのAIDMAルールについて語る

Lushou のシングルページの成功は、多くのウェブマスターにシングルページのウェブサイトを作成する...

偽ジャーナリストが「ビッグV」に変身して数百万ドルをゆすっている。オンラインでのゆすりが産業チェーンを形成している

1980年代生まれの若者は、長い間「全メディア記者」「オピニオンリーダー」の看板を掲げ、ネット上の影...

バックエンドプログラマーに必須: 分散トランザクションの基礎

序文最近、分散トランザクションに関するブログ投稿をいくつか読んで、メモを取りました。ハハハ〜データベ...

Kafka のコアな知識をまとめた記事です。

[[421913]]基本的な紹介Apache Kafka は、LinkedIn が Scala と ...

Weiboマーケティングとは何ですか?特徴は何ですか?

ショートビデオ、セルフメディア、インフルエンサーのためのワンストップサービス新しいメディアの急速な発...

熊張豪は高品質のモバイルコンテンツのみを対象としています

月収10万元の起業の夢を実現するミニプログラム起業支援プランXiong Zhanghao SEO は...

クラウドネイティブの世界のための 7 つの Java フレームワーク

翻訳者 |李睿校正:孫淑娟Java プログラミング言語は 30 年近く前から存在しており、この言語と...

ライトイヤーフォーラム閉鎖の根本的な理由の分析

背景10月、「科学的SEO」という概念の創始者である張国平氏が光年フォーラムの正式な閉鎖を提案し、S...