【翻訳】JAVAインストゥルメント

概要

Package java.lang.instrument: Java プログラミング言語エージェントが JVM 上で実行されるプログラムを計測できるようにするサービスを提供します。検出メカニズムは、メソッドのバイトコードを変更することです。

これには 2 つのインターフェイスが含まれています。

  • ClassFileTransformer
  • Instrumentation

クラス:

  • ClassDefinition

そしていくつかの例外クラス:

  • IllegalClassFormatException
  • UnmodifiableClassException

使い方

コマンドライン起動:

java -javaagent:jarpath[=选项]

jarpath はプロキシ JAR ファイルへのパスです。options はプロキシ オプションです。プロキシ JAR ファイルは、JAR ファイル仕様に準拠する必要があります。

プロキシ JAR ファイルのマニフェストには、属性が含まれている必要がありますPremain-Classこのプロパティの値は、プロキシ クラスの名前です。プロキシ クラスは、premain同様のパブリック静的メソッド (メインと同様) を実装する必要があります。JVM が初期化された後、premain は指定されたプロキシの順序で各メソッドを呼び出し、その後 main が実際のアプリケーション メソッドを呼び出します。起動シーケンスを続行するには、すべての premain メソッドが戻る必要があります。

JVM 実装はプロキシ クラスの呼び出しを試みます。

public static void premain(String agentArgs, Instrumentation inst);

プロキシ クラスがメソッドを実装していない場合は、JVM でメソッドを呼び出してみます。

public static void premain(String agentArgs);

エージェント クラスには、VM の起動後にエージェントを起動するときに使用できる Agentmain メソッドが含まれる場合もあります。このメソッドは、エージェントがコマンド ライン オプションを使用して起動された場合には、agentmain によって呼び出されません。

プロキシ クラスはシステム クラス ローダーによってロードされます。

各エージェントには、agentArgs パラメータを介してエージェント オプションが渡されます。プロキシ オプションは単一の文字列として渡され、追加の解析はプロキシ自体によって実行される必要があります。

プロキシを解決できない場合、JVM は終了します。permain メソッドがキャッチされない例外をスローした場合、JVM は異常終了します。

VM の起動後にエージェントを起動する

実装では、VM の起動後にエージェントを起動するメカニズムが提供される場合があります。開始方法の詳細は実装によって異なりますが、通常、アプリケーションはすでに開始されており、その main メソッドが呼び出されています。実装が VM の起動後のエージェントの起動をサポートしている場合は、以下が適用されます。

  1. エージェント JAR のマニフェストには、Agent-Class 属性が含まれている必要があります。このプロパティの値は、プロキシ クラスの名前です。
  2. エージェント クラスは、パブリックの静的 Agentmain メソッドを実装する必要があります。
  3. システム クラス ローダー ( ClassLoader.getSystemClassLoader ) は、プロキシ JAR ファイルをシステム クラスパスに追加するメカニズムをサポートする必要があります。

プロキシ JAR はシステム クラスパスに追加されます。これは、通常、アプリケーションのメイン メソッドを含むクラスをロードするクラス ローダーです。エージェント クラスがロードされ、JVM は Agentmain メソッドの呼び出しを試行します。JVM はまず、プロキシ クラスで次のメソッドの呼び出しを試みます。

public static void agentmain(String agentArgs, Instrumentation inst);

プロキシ クラスがこのメソッドを実装していない場合、JVM は次の呼び出しを試みます。

public static void agentmain(String agentArgs);

エージェント クラスには、コマンド ライン オプションを使用してエージェントを起動するときに使用する premain メソッドも含まれる場合があります。VM の起動後にエージェントが起動される場合、このメソッドは premain によって呼び出されません。

エージェントは、agentArgs パラメータを通じてエージェント オプションを渡します。プロキシ オプションは単一の文字列として渡され、追加の解析はプロキシ自体によって実行される必要があります。

Agentmain メソッドは、エージェントの起動に必要な初期化を実行する必要があります。起動が完了すると、メソッドは返される必要があります。エージェントの起動に失敗した場合 (たとえば、エージェント クラスをロードできない場合、またはエージェント クラスに適合する Agentmain メソッドがない場合)、JVM は中止されません。Agentmain メソッドがキャッチされない例外をスローした場合、その例外は無視されます。

リスト属性

次のマニフェスト プロパティがエージェント JAR ファイルに対して定義されています。

  • Premain-Class: JVM 起動時にプロキシを指定する場合、このプロパティはプロキシ クラスを指定します。つまり、premain メソッドを含むクラスです。このプロパティは、JVM 起動時にプロキシを指定する場合に必要です。属性が存在しない場合、JVM は異常終了します。注: これはクラス名であり、ファイル名やパスではありません。
  • Agent-Class: VM の起動後にエージェントを起動するメカニズムが実装でサポートされている場合、この属性はエージェント クラスを指定します。つまり、agentmain メソッドを含むクラスです。このプロパティは必須です。これが存在しない場合、エージェントは起動しません。注: これはクラス名であり、ファイル名やパスではありません。
  • Boot-Class-Path: ブート クラス ローダーによって検索されるパスのリスト。パスはディレクトリまたはライブラリ (多くのプラットフォームでは JAR または zip ライブラリと呼ばれることが多い) を表します。これらのパスは、クラスを見つけるためのプラットフォーム固有のメカニズムが失敗した後、ブートストラップ クラス ローダーによって検索されます。パスはリストされた順序で検索されます。リスト内のパスは 1 つ以上のスペースで区切られます。パスは、階層 URI のパス コンポーネントの構文に従います。パスがスラッシュ文字 (「/」) で始まる場合は絶対パスであり、それ以外の場合は相対パスです。相対パスは、エージェント JAR ファイルの絶対パスに対して解決されます。不正なパスや存在しないパスは無視されます。VM の起動後しばらくしてエージェントを起動する場合、JAR ファイルを表さないパスは無視されます。この属性はオプションです。VM の起動後しばらくしてエージェントを起動する場合、JAR ファイルを表さないパスは無視されます。この属性はオプションです。VM の起動後しばらくしてエージェントを起動する場合、JAR ファイルを表さないパスは無視されます。この属性はオプションです。
  • Can-Redefine-Classes: ブール値 (true または false、大文字と小文字は区別されません)。このプロキシに必要なクラスを再定義することは可能ですか。他の値 true は false とみなされます。このプロパティはオプションであり、デフォルトは false です。
  • Can-Retransform-Classes: ブール値 (true または false、大文字と小文字は区別されません)。このプロキシに必要なクラスを再変換する機能があるかどうか。他の値 true は false とみなされます。このプロパティはオプションであり、デフォルトは false です。
  • Can-Set-Native-Method-Prefix: ブール値 (true または false、大文字と小文字は区別されません)。このプロキシに必要なネイティブ メソッド プレフィックスを設定できるかどうか。他の値 true は false とみなされます。このプロパティはオプションであり、デフォルトは false です。

エージェント JAR ファイルのマニフェストには、Premain-Class 属性と Agent-Class 属性の両方が含まれる場合があります。-javaagent オプションを使用してコマンド ラインでエージェントを起動する場合、Premain-Class 属性はエージェント クラスの名前を指定し、Agent-Class 属性は無視されます。同様に、VM の起動後しばらくしてエージェントが起動する場合、Agent-Class 属性はエージェント クラスの名前を指定します (Premain-Class 属性の値は無視されます)。

クラスファイルトランスフォーマー

プロキシは、クラス ファイルを変換するためのこのインターフェイスの実装を提供します。変換は、クラスが JVM によって定義される前に行われます。

メインメソッド

byte[] transform(ClassLoader loader,
                 String className,
                 Class<?> classBeingRedefined,
                 ProtectionDomain protectionDomain,
                 byte[] classfileBuffer)
          throws IllegalClassFormatException

このメソッドの実装では、提供されたクラス ファイルを変換し、新しい置換ファイルを返す場合があります。

トランスフォーマーには 2 種類あり、Instrumentation.addTransformer(ClassFileTransformerTransformer, boolean canRetransform) のパラメーター canRetransform によって決まります。

  • canRetransform は true であり、再変換が可能です。代表者は再び変身することができます。
  • canRetransform は false であり、再変換はできません。代表者を再変換することはできません。

コンバーターがaddTransformer登録されると、新しいクラス定義および再定義ごとにコンバーターが呼び出されます。
新しいクラスのリクエスト定義はClassLoader.defineClassによって行われます。
トランスフォーマーは、クラスが検証されて適用される前に、リクエストの処理中に呼び出されます。
複数のコンバーター間で呼び出しをチェーンします。つまり、前のコンバーターの結果が後のコンバーターの入力になります。

トランスフォーマーは次の順序で呼び出されます。

  • 再変身不能トランスフォーマー
  • 再変換できないネイティブトランスフォーマー
  • 再変換可能なトランスフォーマー
  • 再変換可能なネイティブトランスフォーマー

再変換の場合、the retransformation incapable transformersこれは呼び出されず、前の変換の結果に置き換えられます。

ネイティブ コンバータは、ClassFileLoadHookJVMT インターフェイスのイベントによって提供されます。

コンバーターに渡されるバイト配列引数の場合classfileBuffer:

  • 新しいクラスの場合は、ClassLoader.defineClass に渡します。
  • クラスの再定義の場合、定義のパラメータが Instrumentation.redefineClasses である Definitions.getDefinitionClassFile()
  • クラスの再変換の場合、新しいクラス定義に渡されるバイト、または再定義されている場合は最後の再定義のバイト、自動的に再適用され、再変換できないトランスフォーマーによって変更されなかったすべての変換。詳細については、Instrumentation.retransformClasses を参照してください。

実装メソッドが変換が必要ないと判断した場合は、null を返す必要があります。それ以外の場合は、新しい byte[] 配列を作成し、入力 classfileBuffer を必要な変換とともにコピーして、新しい配列を返す必要があります。classfileBuffer は入力を変更してはなりません。

再変換と再定義の場合、コンバーターは再定義セマンティクスをサポートする必要があります。初期定義中にコンバーターによって変更されたクラスが後で再変換または再定義される場合、コンバーターは出力クラス ファイル内の 2 番目のクラスがクラス ファイルの 2 番目のクラスと同じであることを確認する必要があります。出力用の最初の正当な再定義クラス ファイル。

コンバーターが例外をスローした場合 (例外はキャッチされません)、後続のコンバーターは引き続き呼び出され、ロード、再定義、または再変換を試行します。したがって、例外をスローすると、null を返すのと同じ効果が得られます。コンバーター コードで未チェックの例外が生成されたときに予期しない動作を防ぐために、コンバーターは Throwable をキャッチできます。コンバーターが classFileBuffer が適切にフォーマットされたクラス ファイルを表していないと判断した場合、IllegalClassFormatException をスローする必要があります。ただし、これは IllegalClassFormatException をスローする必要があります。ただし、これは IllegalClassFormatException をスローする必要があります。 null 同じ効果。フォーマットの破損をログに記録したりデバッグしたりするのに役立ちます。

楽器

このクラスは、Java プログラミング言語コードを計測するために必要なサービスを提供します。インストルメンテーションとは、ツールで使用されるデータを収集するためのメソッドにバイトコードを追加することです。変更は純粋に追加的なものであるため、これらのツールはアプリケーションの状態や動作を変更しません。このような無害なツールの例には、モニタリング エージェント、アナライザー、カバレッジ アナライザー、イベント レコーダなどがあります。
インストルメンテーション インターフェイスのインスタンスを取得するには、次の 2 つの方法があります。

  • JVM が指定されたプロキシ クラスで起動するとき。この場合、Instrumentation インスタンスがプロキシ クラスの premain メソッドに渡されます。
  • JVM が、JVM の起動後にエージェントを起動するメカニズムを提供する場合。この場合、Instrumentation インスタンスはエージェント コードの Agentmain メソッドに渡されます。

これらのメカニズムはパッケージ仕様に記載されています。

プロキシが Instrumentation インスタンスを取得すると、プロキシはいつでもそのインスタンスのメソッドを呼び出すことができます。

メインメソッド

  • addTransformer はトランスフォーマーを登録します
  • appendToBootStrapClassLoaderSearch は、ブートストラップ クラス ローダーによって定義される検出クラスを含む JAR ファイルを指定します。
  • appendToSystemClassLoaderSearch は、システム クラス ローダーによって定義された検出クラスを含む JAR ファイルを指定します。
  • getAllLoadedClasses は、現在 JVM によってロードされているすべてのクラスの配列を返します。
  • getInitiatedClasses はすべてのクラスの配列を返し、パラメータ ローダーは起動ローダーです。
  • getObjectSize 指定されたオブジェクトによって消費されるストレージ量の実装固有の近似値を返します。
  • isModifiableClass は、再変換または再定義によってクラスを変更できるかどうかを決定します。
  • isNativeMethodPrefixSupported は、現在の JVM 構成がネイティブ メソッド プレフィックスの設定をサポートしているかどうかを返します。
  • isRedefineClassesSupported は、現在の JVM 構成がクラスの再定義をサポートしているかどうかを返します。
  • isRetransformClassesSupported は、現在の JVM 構成がクラスの再変換をサポートしているかどうかを返します。
  • redefineClasses は、提供されたクラス ファイルを使用して、提供されたクラスのセットを再定義します。
  • RemoveTransformer は、提供されたトランスフォーマーの登録を解除します。
  • retransformClasses は、提供されたクラスのセットを再変換します。
  • setNativeMethodPrefix このメソッドは、名前に適用されるプレフィックスの再試行を許可することにより、ネイティブ メソッド解決の失敗処理を変更します。

参照文書

https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/package-summary.html

おすすめ

転載: blog.csdn.net/sayWhat_sayHello/article/details/121037591