目次
1. Java リフレクションとは何ですか?何に使用されますか?
2. Java クラス ファイルの構造
3. Javaクラスロードのプロセス
3.1. クラスローダーのロードプロセス
3.2. 初期化プロセス
4. ネイティブでのリフレクションの実装
4.1. Class.forName の実装
JDK ソース コードでは、
オブジェクトフラグがネイティブの場合はnative_handlerに渡す 処理
Method.invokeを通じてコン​​ストラクタ(
5. 附录
5.1. JVM与源码阅读工具的选择
5.2. 关于几个ClassLoader
5.3. 反射是否慢?
ホームページ Java &#&チュートリアル JVM での Java リフレクションの実装

JVM での Java リフレクションの実装

Feb 20, 2017 am 10:36 AM
java jvm 反射

1. Java リフレクションとは何ですか?何に使用されますか?

リフレクションにより、プログラム コードが JVM にロードされたクラスの内部情報にアクセスできるようになり、ソース コード内ではなく、作成中および実行中に選択されたクラスとコードを連携できるようになり、開発効率を運用効率に変える手段となります。このため、リフレクションは柔軟なアプリケーションを構築するための主要なツールになります。

リフレクションでは次のことが可能です:

ブラック テクノロジーを実現するためにいくつかのプライベート メソッドを呼び出します。たとえば、デュアル SIM テキスト メッセージの送信、ステータス バーの色の設定、電話の自動切断などです。

POのORM、Json解析などのシリアル化と逆シリアル化を実装します。

JDKでのSocketImplの実装など、クロスプラットフォーム互換性を実現

xmlやアノテーションを通じて、依存性注入(DI)、アノテーション処理、動的プロキシ、単体テストなどの機能が実装されます。 Retrofit、Spring、Dagger など

2. Java クラス ファイルの構造

*.class ファイルでは、クラスは一連のロードと解析の後にバイト ストリームの形式で保存されます。実際にマッピングされるのは下の図の構造で、

javap
ログイン後にコピー

コマンドまたは IDE プラグインを使用してここで表示できます。

typedef struct {
    u4             magic;/*0xCAFEBABE*/
    u2             minor_version; /*网上有表可查*/
    u2             major_version; /*网上有表可查*/
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    //重要
    u2             fields_count;
    field_info     fields[fields_count];
    //重要
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}ClassBlock;
ログイン後にコピー

定数プール: C の DATA セグメントや BSS セグメントと同様に、定数、文字列、メソッド名、その他の値またはシンボルのストレージを提供します (オフセット固定値を持つポインターとみなすことができます)

access_flags : Class

 typedef enum {
      ACC_PUBLIC = 0x0001,
      ACC_FINAL = 0x0010,
      ACC_SUPER = 0x0020,
      ACC_INTERFACE = 0x0200,
      ACC_ACSTRACT = 0x0400
  }AccessFlag
ログイン後にコピー

このクラス/スーパークラス/インターフェイスのフラグ変更: 定数プール内の実際のアドレスを指す、長さ u2 のポインター。リンクフェーズ中に逆参照されます。

filed: フィールド情報、構造は次のとおりです

typedef struct fieldblock {
     char *name;
     char *type;
     char *signature;
     u2 access_flags;
     u2 constant;
     union {
         union {
             char data[8];
             uintptr_t u;
             long long l;
             void *p;
             int i;
         } static_value; 
         u4 offset;
     } u;
  } FieldBlock;
ログイン後にコピー

メソッド: 記述子、access_flags、Code などのインデックスを提供し、定数プールを指します:

その構造は次のとおり、詳細はこちら

 method_info {
      u2             access_flags;
      u2             name_index;
      //the parameters that the method takes and the 
      //value that it return
      u2             descriptor_index;
      u2             attributes_count;
      attribute_info attributes[attributes_count];
  }
ログイン後にコピー
以上具体内容可以参考

JVM文档

周志明的《深入理解Java虚拟机》,少见的国内精品书籍

一些国外教程的解析
ログイン後にコピー


3. Javaクラスロードのプロセス

クラスロードは主に2つのステップに分かれています

最初のステップはClassLoaderを介して読み取って接続することです

2番目のステップはクラスを初期化することです

<clinit>()
ログイン後にコピー



3.1. クラスローダーのロードプロセス

ClassLoader は、純粋な Java またはネイティブを通じて実装できるクラスのロード、接続、およびキャッシュに使用されます。 JVM のネイティブ コードでは、ClassLoader は内部でスレッドセーフな

HashTable<String,Class>
ログイン後にコピー

を維持します。これは、クラス バイト ストリームをデコードした後にキャッシュを実装するために使用されます。HashTable にキャッシュがすでに存在する場合は、キャッシュが直接返されます。それ以外の場合は、クラスの取得後 名前が作成された後、ファイルおよびネットワーク上のクラス バイト ストリームが JVM 内のネイティブ C 構造に逆シリアル化され、メモリが割り当てられ、ポインタが HashTable にキャッシュされます。

以下は配列以外の場合の ClassLoader のプロセスです

find/load: ファイルを C 構造体に逆シリアル化します。

JVM での Java リフレクションの実装

クラス逆シリアル化プロセス

link: クラス構造定数プールに基づいてシンボルを逆参照します。例えば、オブジェクト計算のメモリ空間、メソッドテーブルの作成、ネイティブインボーカー、インターフェースメソッドテーブル、ファイナライザー関数など。

3.2. 初期化プロセス

ClassLoaderがクラスのロードを完了すると、クラスが初期化されます。主に

<clinit()>
ログイン後にコピー

の静的コードセグメントと静的変数を実行します (ソースコードのシーケンスに応じて)。

public class Sample {
  //step.1
  static int b = 2;
  //step.2
  static {
    b = 3;
  }
  public static void main(String[] args) {
    Sample s = new Sample();
    System.out.println(s.b);
    //b=3
  }
}
ログイン後にコピー
具体参考如下:

When and how a Java class is loaded and initialized?

The Lifetime of a Type
ログイン後にコピー


初期化が完了したら、オブジェクト

<init>
ログイン後にコピー

の構築ですが、この記事では説明しません。

4. ネイティブでのリフレクションの実装

リフレクションは Java で直接呼び出すことができますが、最後の呼び出しは依然としてネイティブ メソッドです。以下は、主流のリフレクション操作の実装です。

4.1. Class.forName の実装

Class.forName は、

Class.forName("java.lang.String")
ログイン後にコピー

などのパッケージ名を通じて Class オブジェクトを検索できます。

JDK ソース コードの実装では、ネイティブ メソッド

forName0()
ログイン後にコピー

が JVM で呼び出される実際のメソッドは、ClassLoader プロセスと同じです。上で紹介されています。



4.2. getDeclaredFields の実装

JDK ソース コードでは、

findClassFromClassLoader()
ログイン後にコピー

メソッドが実際に JVM でのネイティブ メソッド

class.getDeclaredFields()
ログイン後にコピー

を呼び出すことがわかります。クラス構造情報、

getDeclaredFields0()
ログイン後にコピー

および

field_count
ログイン後にコピー
ログイン後にコピー

フィールドを取得します。このフィールドはロードプロセス中にすでに配置されています

メモリを割り当て、

fields[]
ログイン後にコピー
ログイン後にコピー



のサイズに応じて配列を作成します 配列に対してforEachループを実行します

field_count
ログイン後にコピー
ログイン後にコピー

の情報を基にObjectオブジェクトを順番に作成します

fields[]
ログイン後にコピー
ログイン後にコピー




4.3 Method.invokeの実装

以下は同期せずに例外なく呼び出す手順です


フレームを作成

オブジェクトフラグがネイティブの場合はnative_handlerに渡す 処理

フレーム内のJavaコードを実行

フレームをポップアップ

実行結果のポインタを返す


主要慢在如下方面

创建、计算、分配数组对象

对字段进行循环赋值
ログイン後にコピー




4.4の実装。 class.newInstanceの

権限、事前に割り当てられた領域サイズ、その他のパラメータを検出


オブジェクトオブジェクトを作成し、領域を割り当てる

Method.invokeを通じてコン​​ストラクタ(

主要慢在如下方面

需要完全执行ByteCode而缺少JIT等优化

检查参数非常多,这些本来可以在编译器或者加载时完成
ログイン後にコピー

)を呼び出す

オブジェクトポインタを返す

<init>()
ログイン後にコピー



5. 附录

5.1. JVM与源码阅读工具的选择

初次学习JVM时,不建议去看Android Art、Hotspot等重量级JVM的实现,它内部的防御代码很多,还有android与libcore、bionic库紧密耦合,以及分层、内联甚至能把编译器的语义分析绕进去,因此找一个教学用的、嵌入式小型的JVM有利于节约自己的时间。因为以前折腾过OpenWrt,听过有大神推荐过jamvm,只有不到200个源文件,非常适合学习。

在工具的选择上,个人推荐SourceInsight。对比了好几个工具clion,vscode,sublime,sourceinsight,只有sourceinsight对索引、符号表的解析最准确。

5.2. 关于几个ClassLoader

参考这里

ClassLoader0:native的classloader,在JVM中用C写的,用于加载rt.jar的包,在Java中为空引用。

ExtClassLoader: 用于加载JDK中额外的包,一般不怎么用

AppClassLoader: 加载自己写的或者引用的第三方包,这个最常见

例子如下

//sun.misc.Launcher$AppClassLoader@4b67cf4d
//which class you create or jars from thirdParty
//第一个非常有歧义,但是它的确是AppClassLoader
ClassLoader.getSystemClassLoader();
com.test.App.getClass().getClassLoader();
Class.forName("ccom.test.App").getClassLoader()
//sun.misc.Launcher$ExtClassLoader@66d3c617
//Class loaded in ext jar
Class.forName("sun.net.spi.nameservice.dns.DNSNameService")
//null, class loaded in rt.jar
String.class.getClassLoader()
Class.forName("java.lang.String").getClassLoader()
Class.forName("java.lang.Class").getClassLoader()
Class.forName("apple.launcher.JavaAppLauncher").getClassLoader()
ログイン後にコピー

最后就是

getContextClassLoader()
ログイン後にコピー

,它在Tomcat中使用,通过设置一个临时变量,可以向子类ClassLoader去加载,而不是委托给ParentClassLoader

ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
try {
    Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
    // call some API that uses reflection without taking ClassLoader param
} finally {
    Thread.currentThread().setContextClassLoader(originalClassLoader);
}
ログイン後にコピー

最后还有一些自定义的ClassLoader,实现加密、压缩、热部署等功能,这个是大坑,晚点再开。

5.3. 反射是否慢?

在Stackoverflow上认为反射比较慢的程序员主要有如下看法

验证等防御代码过于繁琐,这一步本来在link阶段,现在却在计算时进行验证

产生很多临时对象,造成GC与计算时间消耗

由于缺少上下文,丢失了很多运行时的优化,比如JIT(它可以看作JVM的重要评测标准之一)

当然,现代JVM也不是非常慢了,它能够对反射代码进行缓存以及通过方法计数器同样实现JIT优化,所以反射不一定慢。

更重要的是,很多情况下,你自己的代码才是限制程序的瓶颈。因此,在开发效率远大于运行效率的的基础上,大胆使用反射,放心开发吧。

 以上就是Java反射在JVM的实现 的内容,更多相关内容请关注PHP中文网(www.php.cn)!


このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Javaの完全数 Javaの完全数 Aug 30, 2024 pm 04:28 PM

Java における完全数のガイド。ここでは、定義、Java で完全数を確認する方法、コード実装の例について説明します。

ジャワのウェカ ジャワのウェカ Aug 30, 2024 pm 04:28 PM

Java の Weka へのガイド。ここでは、weka java の概要、使い方、プラットフォームの種類、利点について例を交えて説明します。

Javaのスミス番号 Javaのスミス番号 Aug 30, 2024 pm 04:28 PM

Java のスミス番号のガイド。ここでは定義、Java でスミス番号を確認する方法について説明します。コード実装の例。

Java Springのインタビューの質問 Java Springのインタビューの質問 Aug 30, 2024 pm 04:29 PM

この記事では、Java Spring の面接で最もよく聞かれる質問とその詳細な回答をまとめました。面接を突破できるように。

Java 8 Stream Foreachから休憩または戻ってきますか? Java 8 Stream Foreachから休憩または戻ってきますか? Feb 07, 2025 pm 12:09 PM

Java 8は、Stream APIを導入し、データ収集を処理する強力で表現力のある方法を提供します。ただし、ストリームを使用する際の一般的な質問は次のとおりです。 従来のループにより、早期の中断やリターンが可能になりますが、StreamのForeachメソッドはこの方法を直接サポートしていません。この記事では、理由を説明し、ストリーム処理システムに早期終了を実装するための代替方法を調査します。 さらに読み取り:JavaストリームAPIの改善 ストリームを理解してください Foreachメソッドは、ストリーム内の各要素で1つの操作を実行する端末操作です。その設計意図はです

Java での日付までのタイムスタンプ Java での日付までのタイムスタンプ Aug 30, 2024 pm 04:28 PM

Java での日付までのタイムスタンプに関するガイド。ここでは、Java でタイムスタンプを日付に変換する方法とその概要について、例とともに説明します。

カプセルの量を見つけるためのJavaプログラム カプセルの量を見つけるためのJavaプログラム Feb 07, 2025 am 11:37 AM

カプセルは3次元の幾何学的図形で、両端にシリンダーと半球で構成されています。カプセルの体積は、シリンダーの体積と両端に半球の体積を追加することで計算できます。このチュートリアルでは、さまざまな方法を使用して、Javaの特定のカプセルの体積を計算する方法について説明します。 カプセルボリュームフォーミュラ カプセルボリュームの式は次のとおりです。 カプセル体積=円筒形の体積2つの半球体積 で、 R:半球の半径。 H:シリンダーの高さ(半球を除く)。 例1 入力 RADIUS = 5ユニット 高さ= 10単位 出力 ボリューム= 1570.8立方ユニット 説明する 式を使用してボリュームを計算します。 ボリューム=π×R2×H(4

未来を創る: まったくの初心者のための Java プログラミング 未来を創る: まったくの初心者のための Java プログラミング Oct 13, 2024 pm 01:32 PM

Java は、初心者と経験豊富な開発者の両方が学習できる人気のあるプログラミング言語です。このチュートリアルは基本的な概念から始まり、高度なトピックに進みます。 Java Development Kit をインストールしたら、簡単な「Hello, World!」プログラムを作成してプログラミングを練習できます。コードを理解したら、コマンド プロンプトを使用してプログラムをコンパイルして実行すると、コンソールに「Hello, World!」と出力されます。 Java の学習はプログラミングの旅の始まりであり、習熟が深まるにつれて、より複雑なアプリケーションを作成できるようになります。

See all articles