JVMにおけるJavaポリモーフィズムは次のようになります

JVMにおけるJavaポリモーフィズムは次のようになります

多態性

オブジェクト指向プログラミング言語では、「ポリモーフィズム」は重要な概念です。オブジェクト指向の本質はメソッドとデータの結合にあるとよく言われます。継承関係を持つクラスの場合、メソッド バインディングは最終的に子クラスが親クラス メソッドを「オーバーライド」し、親クラス参照を通じて子クラス オブジェクトを指すことで、実行時ポリモーフィズムを実現します。

[[327572]]

説明するのは少しわかりにくいので、まずは Hello World に次いで有名な「Animal - Dog」コードを使用してポリモーフィズムを説明し、次に JVM レベルでポリモーフィズムがどのように実装されているかを分析します。

  1. パッケージ com.example.demo;
  2. パブリッククラスDemo {
  3. 公共 静的void main(String[] args) {
  4. 動物a = 新しい動物();
  5. 言う();
  6. Dog d = 新しい Dog();
  7. d.言う();
  8. 動物広告 = new Dog();
  9. 広告を言う();
  10. }
  11. }
  12. クラス動物{
  13. パブリックボイドセイ(){
  14. システム。 out .println( "動物が言う" );
  15. }
  16. パブリックボイドプレイ(){
  17. システム。 .println ( "再生..." );
  18. }
  19. }
  20. クラスDogはAnimalを拡張します{
  21. パブリックボイドセイ(){
  22. システム。 out .println( "犬が言う" );
  23. }
  24. }

この出力は、オブジェクト指向プログラミングに慣れている Java 開発者には馴染みのあるものです。

  1. 動物の言うこと
  2. 犬が言う
  3. 犬が言う

では、仮想マシンは Animal の say を呼び出すべきか Dog の say を呼び出すべきかをどうやって知るのでしょうか?

バイトコードレベルから見てみましょう。

  1. 0 新しい #2 <com/example/demo/Animal>
  2. 3 重複
  3. 4 呼び出しスペシャル #3 <com/example/demo/Animal.<init>>
  4. 7 アストア1
  5. 8 アロード1
  6. 9 仮想関数 #4 <com/example/demo/Animal.say>
  7. 12 新規 #5 <com/example/demo/Dog>
  8. 15 重複
  9. 16 呼び出しスペシャル #6 <com/example/demo/Dog.<init>>
  10. 19 アストア2
  11. 20 アロード2
  12. 21 仮想関数を呼び出す #7 <com/example/demo/Dog.say>
  13. 24 新規 #5 <com/example/demo/Dog>
  14. 27 重複
  15. 28 呼び出しスペシャル #6 <com/example/demo/Dog.<init>>
  16. 31 アストア3
  17. 32 アロード3
  18. 33 仮想呼び出し #4 <com/example/demo/Animal.say>
  19. 36戻る 

バイトコードの 9 行目と 33 行目はそれぞれ d.say() と ad.say() に対応していますが、命令の内容は実際には同じであることに気付きましたか。それはすごいですね。

これら 2 つのメソッドが実行される前に、8 行目と 32 行目に aload 操作が行われ、これら 2 つのオブジェクトの参照がスタックの先頭にプッシュされ、後続の操作で使用できるようになります。これら 2 つのオブジェクトは、一般にメソッドのレシーバーと呼ばれます。 Golang などの言語に精通している友人にとっては、この概念は馴染み深いものです。

9 行目と 33 行目から、メソッド呼び出しのバイトコード命令とパラメーターの両方が定数プールの項目 4 を指していることがわかります。すべて同じですが、最終結果は同じではありません。ここでの焦点は、invokevirtual 命令の多態的ポインタ検索プロセス、つまり、オブジェクトの vtable に基づいて実行時にメソッドを見つけることです。

vtable とは何ですか?

前回の内容でも述べたように、命令が実行されると、スタックの上から現在のメソッドの「レシーバー」が取得され、invokerirtual を通じてこのレシーバーに対応するメソッドが実行されます。ここでの「仮想」という単語は、C++ の仮想メソッドに似ていることに注意してください。これについては話さずに、Java についてだけ話しましょう。

すべてのオブジェクトには独自の「メソッド テーブル」があり、これには独自のメソッドだけでなく、親クラスから継承されたメソッドやオーバーライドされた親クラスのメソッドも含まれます。そのため、オーバーライドとオーバーロードに対応して、メソッドテーブルにも違いが反映されます。各サブクラスが親クラスから継承すると、親クラスのメソッド テーブルのコピーが直接コピーされます。親クラスのメソッドをオーバーライドする場合、メソッド テーブル内の同じ順序のメソッドが直接更新されます。

オーバーロードは、シグネチャとパラメータの違いにより本質的に新しいメソッドとなり、メソッド テーブルに新しい要素が追加されます。

ここでのメソッド テーブルは、vtable (Virtual Method Table) と呼ばれます。表内の各メソッドは、実際の実行エントリ アドレスに対応しています。書き換えがない場合、親クラスと子クラスのアドレスは同じになり、両方とも親クラスの実装を指します。

サブクラスがメソッドをオーバーライドする場合、サブクラス メソッド テーブル内のこのメソッドのアドレスは、そのサブクラスが実装したバージョンを指します。

上記のバイトコードからわかるように、2 つのinvokevirtuals に対応する定数プールのインデックス番号は同じです。したがって、実装タイプを変更する場合は、メソッド テーブルを見つけるためにオブジェクトを変更するだけでよく、インデックスは同じままです。

観察する

Java プロセスにアタッチするには、コードにラッチを追加して awiat ブロッキングを実行し、SA を起動して監視します。

クラスブラウザを選択

上記で作成したオブジェクトはクラス リストにあります。 @ 記号の後には、このオブジェクトに対応するメモリ アドレスが続きます。 Dog アドレスをコピーし、メニューから Inspector を選択します。

_vtable_len: 7 が表示されます

これは、vtable の長さが 7 であり、その中に 7 つのメソッドがあることを示しています。

実際、このクラスでは親クラス Animal の say メソッドのみを書き換えます。その他は、Animal から継承された play メソッドと、スーパークラス Object の 5 つのメソッドです。次のようになります:

JVM はクラスを初めてロードするときに、クラスに含まれるメソッドを解析し、メソッドが解析された後に現在のクラス vtable のサイズを計算します。

Object クラスには 5 つ以上のメソッドがあるのに、なぜ 5 つだけがカウントされるのかと疑問に思うかもしれません。では、追加する他の静的メソッドや最終メソッドについてはどうでしょうか?

ここで、vtable は非静的な最終値のみを計算し、すべての計算が完了した後に vtable_len の値が取得されます。

各 Java クラスには JVM 内に独自の instanceKlass があり、vtable はこの instanceKlass の最後に割り当てられます。

64 ビット システムでは、instanceKlass 全体のサイズは 0x1b8 です。覚えておいてください。後で必要になります。上で Dog クラスのメモリ アドレスを確認しましたが、検索を続けると、他のメソッドに対応するメモリ アドレスを確認できます。

Windows -> コンソールでこれを実行します:

  1. メモリ 0x7C0060DD0 7

この値はどこから来るのでしょうか?これは、オブジェクトのメモリ アドレスと instanceKlass のサイズを加算した値から始まります。

  1. 0x7C0060DD0 = 0x00000007c0060c18 + 0x1b8

7 つの方法があるため、7 つのアドレスを順番に検索します。

したがって、Java の対応するオーバーライドされたメソッドは、クラスがロードされたときにどの特定のメソッドに対応するかを知るためのものであり、動的バインディングまたは遅延バインディングとも呼ばれることがわかります。

要約すると、ここでの vtable は、アイアンマンのさまざまなスキルなど、すべての能力をリストするツール リストに相当します。各機能は特定の超能力を指します。私たちのコードでは、これは配列として理解でき、配列の各要素はメソッドのアドレスを指します。

興味があれば、静的メソッドを追加して、ここにあるかどうかを自分で調べることができますか?結局のところ、静的メソッドを実行するためのinvokestatic命令はないのでしょうか?

この記事はWeChatの公開アカウント「Tomcat Things」から転載したものです。以下のQRコードからフォローできます。この記事を転載する場合は、Tomcat Things の公開アカウントにご連絡ください。

<<:  高性能 AMD クラウド ホストを選択するにはどうすればよいでしょうか? AWS、Google Cloud、UCloud、Tencent Cloud テストコンテスト

>>:  マイクロサービス分散アーキテクチャでログリンクトラッキングを実装するにはどうすればよいですか?

推薦する

月収2万元を稼ぐソフト記事シリーズから何を学ぶか

葉鋒が毎月2万元を稼ぐソフト記事4本シリーズは、今年これまでのA5で最も成功したソフト記事と言える。...

WeChatプロモーション活動を通じて新規ユーザーを獲得するには?

まず最初に、私がこの要約を書いた理由を紹介したいと思います。 1年前、私はAlibabaやVipsh...

テクノロジーに長けた劉強東氏が、なぜテクノロジーをJD.comのボトルネックにしてしまったのか?

数日前の「独身の日」が終わり、技術的な概要と議論が始まりました。 Weiboでは、@IT程序猿が長文...

DevOpsのボトルネックを克服するための4つのステップについてお話ししましょう

挑戦的なビデオ ゲームでレベルアップするために必要な手順は、DevOps の旅でレベルアップするため...

Baidu スナップショットをタイムリーに更新するために注意すべき 4 つの側面

以前作成したいくつかのサイトは、すべてランキングが良好でした。ホームページの一部は途中で何度も Ba...

クラウド支出の無駄を削減する 5 つの方法

「マクロ経済環境がますます厳しくなり、ビジネスリーダーがビジネスの回復力を高める方法を模索する中、C...

見落とされやすく、キーワードランキングに影響を与える要因

長い間、多くの友人がキーワードランキングを上げるために一生懸命働いてきました。彼らは昼夜を問わず外部...

この記事では、仮想マシンの 4 つのネットワーク モデルについて包括的に説明します (画像とテキスト付き)

01. 物理ネットワークから仮想ネットワークへ有名な「六次の隔たり定理」によれば、世界中の見知らぬ人...

Hawkhost: 夏が終わり、すべてのアイテム、仮想ホスト、VPS、クラウド サーバー、シンガポール/香港の 8 つのデータ センターが最大 70% オフ

夏の終わりに、Hawkhost はサイト全体のプロモーションを開始する口実を見つけました。仮想ホステ...

電子商取引の冬:易百モールが閉鎖、「良いプロジェクト」の衰退を振り返る

ショッピングモールの崩壊により、約20の銀行が支払い不履行に対する権利保護の渦に巻き込まれた。消費者...

新たなブランドプロモーションチャネルの模索:モバイルアプリ

【ポイント】 掲示板や新種のSNS、Weiboなどのソーシャルメディアなどの伝統的なインターネットの...

テンセントの第4の流通モデルが登場?最初のものは両方のリストで1位です

7月2日、センチュリー天成とテンセントが共同で運営するレーシングモバイルゲーム「KartRider ...

新しい SaaS ビジネスを構築して成長を促進する 6 つの方法

現在、多くの企業が収益成長の課題に直面しています。2026 年までに収益の最大 50% が新規事業や...

ウェブサイト最適化に関する FAQ 5: ウェブサイトのインデックス数が減少したのはなぜですか?

先ほど、SEO に関する 4 つの基本的な質問と回答について説明しました。 「新しいサイトが含まれな...