Интерфейс Instrumentation
public interface Instrumentation
Есть два способа получить экземпляр интерфейса Instrumentation:
Когда JVM запускается таким образом, что указывает на класс агента. В этом случае экземпляр
Instrumentationпередаётся методуpremainкласса агента.-
Когда JVM предоставляет механизм для запуска агентов некоторое время после запуска JVM. В этом случае экземпляр
Instrumentationпередаётся методуagentmainкода агента.
Эти механизмы описаны в спецификации пакета.
После того, как агент получит экземпляр Instrumentation , агент может вызывать методы экземпляра в любое время.
- Примечание API:
- Этот интерфейс не предназначен для реализации вне модуля java.instrument.
- С:
- 1.5
Краткое описание методов
| Модификатор и тип | Метод | Описание |
|---|---|---|
void |
addTransformer |
Регистрирует предоставленный трансформер. |
void |
addTransformer |
Регистрирует предоставленный трансформер. |
void |
appendToBootstrapClassLoaderSearch |
Указывает JAR-файл с классами инструментации, которые должны быть определены загрузчиком начального класса. |
void |
appendToSystemClassLoaderSearch |
Указывает JAR-файл с классами инструментации, которые должны быть определены системным загрузчиком классов. |
Class[] |
getAllLoadedClasses() |
Возвращает массив всех классов, которые в настоящее время загружены JVM. |
Class[] |
getInitiatedClasses |
Возвращает массив всех классов, которые loader может найти по имени через ClassLoader::loadClass, Class::forName и связь байткодов. |
long |
getObjectSize |
Возвращает приблизительную, зависящую от реализации, оценку объема памяти, занимаемой указанным объектом. |
boolean |
isModifiableClass |
Проверяет, можно ли модифицировать класс с помощью повторной трансформации или переопределения. |
boolean |
isModifiableModule |
Проверяет, можно ли модифицировать модуль с помощью redefineModule. |
boolean |
isNativeMethodPrefixSupported() |
Возвращает, поддерживает ли текущая конфигурация JVM установку префикса для методов нативных. |
boolean |
isRedefineClassesSupported() |
Возвращает, поддерживает ли текущая конфигурация JVM переопределение классов. |
boolean |
isRetransformClassesSupported() |
Возвращает, поддерживает ли текущая конфигурация JVM повторную трансформацию классов. |
void |
redefineClasses |
Переопределяет указанный набор классов, используя предоставленные файлы классов. |
void |
redefineModule |
Переопределяет модуль для расширения набора модулей, которые он читает, набора пакетов, которые он экспортирует или открывает, или сервисов, которые он использует или предоставляет. |
boolean |
removeTransformer |
Отменяет регистрацию предоставленного трансформера. |
void |
retransformClasses |
Повторно преобразует указанный набор классов. |
void |
setNativeMethodPrefix |
Этот метод изменяет обработку ошибок при разрешении нативных методов, позволяя повторную попытку с применением префикса к имени. |
Подробное описание методов
addTransformer
void addTransformer(ClassFileTransformer transformer, boolean canRetransform)
canRetransform равно true, при их повторном преобразовании. ClassFileTransformer определяет порядок вызовов преобразования. Если трансформатор генерирует исключение во время выполнения, JVM всё равно вызовет других зарегистрированных трансформаторов в порядке очереди. Один и тот же трансформатор может быть добавлен более одного раза, но это крайне не рекомендуется — избегайте этого, создавая новый экземпляр класса трансформатора. Этот метод предназначен для использования в инструментировании, как описано в спецификации класса.
- Параметры:
-
transformer- регистрируемый трансформатор -
canRetransform- можно ли повторно преобразовывать преобразования этого трансформатора - Исключения:
-
NullPointerException- если переданnullтрансформатор -
UnsupportedOperationException- еслиcanRetransformравно true, а текущая конфигурация JVM не позволяет повторное преобразование (isRetransformClassesSupported()равно false) - С:
- 1.6
addTransformer
void addTransformer(ClassFileTransformer transformer)
Аналогично addTransformer(transformer, false).
- Параметры:
-
transformer- регистрируемый трансформатор - Исключения:
-
NullPointerException- если переданnullтрансформатор - См. также:
removeTransformer
boolean removeTransformer(ClassFileTransformer transformer)
- Параметры:
-
transformer- дерегистрируемый трансформатор - Возвращает:
- true, если трансформатор был найден и удалён, false — если трансформатор не был найден
- Исключения:
-
NullPointerException- если переданnullтрансформатор
isRetransformClassesSupported
boolean isRetransformClassesSupported()
Can-Retransform-Classes манифеста установлено в true в JAR-файле агента (как описано в спецификации пакета) и JVM поддерживает эту возможность. В течение одной инстанции одной JVM, множественные вызовы этого метода всегда вернут один и тот же ответ.- Возвращает:
- true, если текущая конфигурация JVM поддерживает повторное преобразование классов, false — если нет.
- С:
- 1.6
- См. также:
retransformClasses
void retransformClasses(Class<?>... classes) throws UnmodifiableClassException
Эта функция способствует инструментированию уже загруженных классов. Когда классы загружаются первоначально или когда они переопределяются, начальные байты файла класса могут быть преобразованы с помощью ClassFileTransformer. Эта функция повторно выполняет процесс преобразования (независимо от того, происходило ли преобразование ранее). Это повторное преобразование выполняется следующими шагами:
- начиная с начальных байтов файла класса
- для каждого трансформатора, который был добавлен с
canRetransformравным false, байты, возвращённые методомtransformво время последней загрузки или переопределения класса, используются как результат преобразования; обратите внимание, что это эквивалентно повторному применению предыдущего преобразования без изменений; за исключением того, что методtransformне вызывается. - для каждого трансформатора, который был добавлен с
canRetransformравным true, методtransformвызывается в этих трансформаторах - преобразованные байты файла класса устанавливаются в качестве нового определения класса
Порядок преобразования описан в ClassFileTransformer. Этот же порядок используется при автоматическом повторном применении трансформаций, не способных к повторному преобразованию.
Первоначальные байты файла класса представляют собой байты, переданные ClassLoader.defineClass или redefineClasses (до применения каких-либо трансформаций), однако они могут не совпадать точно. Пул констант может не иметь того же расположения или содержимого. Пул констант может иметь больше или меньше элементов. Элементы пула констант могут быть в другом порядке; однако, индексы пула констант в байткодах методов будут соответствовать. Некоторые атрибуты могут отсутствовать. Где порядок не имеет значения, например порядок методов, порядок может не сохраняться.
Этот метод работает с набором, чтобы позволить взаимозависимым изменениям более чем одного класса одновременно (повторное преобразование класса А может потребовать повторное преобразование класса Б).
Если переобразованный метод имеет активные кадры стека, эти активные кадры продолжают выполнять байткоды исходного метода. Переобразованный метод будет использоваться при новых вызовах.
Этот метод не вызывает никакой инициализации, кроме той, которая произошла бы в соответствии со стандартной семантикой JVM. Другими словами, переопределение класса не приводит к выполнению его инициализаторов. Значения статических переменных останутся такими, какими они были до вызова.
Экземпляры переобразованного класса не затрагиваются.
Поддерживаемые изменения файла класса описаны в JVM TI RetransformClasses. Байты файла класса не проверяются, не верифицируются и не устанавливаются до тех пор, пока не будут применены трансформации, если результирующие байты содержат ошибку, этот метод сгенерирует исключение.
Если этот метод генерирует исключение, ни один класс не был переобразован.
Этот метод предназначен для использования в инструментировании, как описано в спецификации класса.
- Параметры:
-
classes- массив классов для повторного преобразования; разрешён массив нулевой длины, в этом случае этот метод ничего не делает - Исключения:
-
UnmodifiableClassException- если указанный класс не может быть изменён (isModifiableClass(java.lang.Class<?>)вернётfalse) -
UnsupportedOperationException- если текущая конфигурация JVM не допускает повторное преобразование (isRetransformClassesSupported()равно false) или попытка повторного преобразования сделала недопустимые изменения -
ClassFormatError- если данные не содержали допустимый класс -
NoClassDefFoundError- если имя в файле класса не равно имени класса -
UnsupportedClassVersionError- если номера версий файла класса не поддерживаются -
ClassCircularityError- если новые классы содержат цикличность -
LinkageError- если произошла ошибка связывания -
NullPointerException- если предоставленный массив классов или любой из его компонентовnull. - С:
- 1.6
- См. также:
isRedefineClassesSupported
boolean isRedefineClassesSupported()
Can-Redefine-Classes манифеста установлено в true в JAR-файле агента (как описано в спецификации пакета) и JVM поддерживает эту возможность. В течение одной инстанции одной JVM, множественные вызовы этого метода всегда вернут один и тот же ответ.- Возвращает:
- true, если текущая конфигурация JVM поддерживает переопределение классов, false — если нет.
- См. также:
переопределитьКлассы
void redefineClasses(ClassDefinition... definitions) throws ClassNotFoundException, UnmodifiableClassException
Этот метод используется для замены определения класса без ссылки на байты существующего файла класса, как это можно сделать при повторной компиляции из исходного кода для отладки с исправлением и продолжением. В тех случаях, когда существующие байты файла класса должны быть преобразованы (например, в инструментировании байткода) retransformClasses следует использовать.
Этот метод работает со множеством, чтобы разрешить взаимозависимые изменения более чем в одном классе одновременно (переопределение класса A может потребовать переопределения класса B).
Если переопределенный метод имеет активные фреймы стека, эти активные фреймы продолжают выполнение байткода исходного метода. Переопределенный метод будет использоваться при новых вызовах.
Этот метод не вызывает никакой инициализации, кроме той, которая произошла бы в соответствии с обычными семантиками JVM. Другими словами, переопределение класса не приводит к выполнению его инициализаторов. Значения статических переменных останутся такими, какими они были до вызова.
Экземпляры переопределенного класса не затрагиваются.
Поддерживаемые изменения файла класса описаны в JVM TI RedefineClasses. Байты файла класса не проверяются, не верифицируются и не устанавливаются до тех пор, пока не будут применены преобразования, если полученные байты неверны, этот метод выбросит исключение.
Если этот метод выбросит исключение, никакие классы не были переопределены.
Этот метод предназначен для использования в инструментировании, как описано в спецификации класса.
- Parameters:
-
definitions- массив классов для переопределения с соответствующими определениями; разрешен массив нулевой длины, в этом случае этот метод ничего не делает - Throws:
-
UnmodifiableClassException- если указанный класс не может быть изменен (isModifiableClass(java.lang.Class<?>)вернул быfalse) -
UnsupportedOperationException- если текущая конфигурация JVM не допускает переопределения (isRedefineClassesSupported()ложно) или переопределение попыталось выполнить недопустимые изменения -
ClassFormatError- если данные не содержали допустимого класса -
NoClassDefFoundError- если имя в файле класса не равно имени класса -
UnsupportedClassVersionError- если номера версий файла класса не поддерживаются -
ClassCircularityError- если новые классы содержат цикличность -
LinkageError- если произошла ошибка привязке -
NullPointerException- если предоставленный массив определений или любой из его компонентовnull -
ClassNotFoundException- никогда не может быть выброшено (присутствует только для совместимости) - See Also:
isModifiableClass
boolean isModifiableClass(Class<?> theClass)
true. Если класс не изменяемый, этот метод возвращает false. Для того, чтобы класс можно было повторно преобразовать, isRetransformClassesSupported() также должно быть истинно. Но значение isRetransformClassesSupported() не влияет на значение, возвращаемое этой функцией. Для того, чтобы класс можно было переопределить, isRedefineClassesSupported() также должно быть истинно. Но значение isRedefineClassesSupported() не влияет на значение, возвращаемое этой функцией.
Примитивные классы (например, java.lang.Integer.TYPE) и массивы никогда не изменяемы.
- Parameters:
-
theClass- класс, который нужно проверить на возможность изменения - Returns:
- можно ли изменить аргумент класса
- Throws:
-
NullPointerException- если указанный классnull. - Since:
- 1.6
- See Also:
getAllLoadedClasses
Class[] getAllLoadedClasses()
- Returns:
- массив, содержащий все классы, загруженные JVM, нулевой длины, если их нет
getInitiatedClasses
Class[] getInitiatedClasses(ClassLoader loader)
loader может найти по имени через ClassLoader::loadClass, Class::forName и привязку байткода. То есть, все классы, для которых loader был зарегистрирован как инициализирующий загрузчик. Если предоставленный loader null, возвращаются классы, которые загрузчик bootstrap может найти по имени. Возвращаемый массив не включает скрытые классы или интерфейсы или массивы, элементный тип которых является скрытым классом или интерфейсом, так как они не могут быть обнаружены никаким загрузчиком классов.
- Parameters:
-
loader- загрузчик, список инициализированных классов которого будет возвращен - Returns:
- массив, содержащий все классы, которые
loaderможет найти по имени; нулевой длины, если их нет
getObjectSize
long getObjectSize(Object objectToSize)
- Parameters:
-
objectToSize- объект, размер которого нужно получить - Returns:
- зависящая от реализации приблизительная оценка объема памяти, занимаемой указанным объектом
- Throws:
-
NullPointerException- если предоставленный объектnull.
appendToBootstrapClassLoaderSearch
void appendToBootstrapClassLoaderSearch(JarFile jarfile)
Когда встроенный загрузчик классов виртуальной машины, известный как «загрузчик bootstrap», безуспешно ищет класс, записи в JAR file также будут просмотрены.
Этот метод может использоваться несколько раз для добавления нескольких JAR-файлов, которые будут просматриваться в порядке вызова этого метода.
Агент должен позаботиться о том, чтобы JAR-файл не содержал классов или ресурсов, кроме тех, которые должны быть определены загрузчиком bootstrap для целей инструментирования. Несоблюдение этого предупреждения может привести к неожиданному поведению, которое сложно диагностировать. Например, предположим, что есть загрузчик L, и родитель L для делегирования — загрузчик bootstrap. Кроме того, метод в классе C, классе, определенном L, ссылается на класс-аксессор C$1. Если JAR-файл содержит класс C$1, то делегирование загрузчику bootstrap приведет к определению C$1 загрузчиком bootstrap. В этом примере будет брошено IllegalAccessError , что может привести к сбою приложения. Один из способов избежать таких проблем — использовать уникальное имя пакета для классов инструментирования.
Спецификация виртуальной машины Java определяет, что последующая попытка разрешить символическую ссылку, которую виртуальная машина Java ранее безуспешно пыталась разрешить, всегда завершается той же ошибкой, что и в результате первой попытки разрешения. Следовательно, если JAR-файл содержит запись, соответствующую классу, для которого виртуальная машина Java безуспешно пыталась разрешить ссылку, последующие попытки разрешить эту ссылку завершатся той же ошибкой, что и первая попытка.
- Parameters:
-
jarfile- JAR-файл, который будет просматриваться, когда загрузчик bootstrap безуспешно ищет класс. - Throws:
-
NullPointerException- еслиjarfilenull. - Since:
- 1.6
- See Also:
appendToSystemClassLoaderSearch
void appendToSystemClassLoaderSearch(JarFile jarfile)
getSystemClassLoader()) неудачно ищет класс, записи в JarFile также будут просмотрены. Этот метод можно использовать несколько раз для добавления нескольких JAR-файлов, которые будут просматриваться в порядке вызова этого метода.
Агент должен позаботиться о том, чтобы JAR-файл не содержал никаких классов или ресурсов, кроме тех, которые должны быть определены загрузчиком системных классов для целей инструментирования. Несоблюдение этого предупреждения может привести к неожиданному поведению, которое сложно диагностировать (см. appendToBootstrapClassLoaderSearch).
Загрузчик системных классов поддерживает добавление JAR-файла для поиска, если он реализует метод с именем appendToClassPathForInstrumentation, который принимает один параметр типа java.lang.String. Метод не обязан иметь public доступ. Имя JAR-файла получается путем вызова метода getName() для jarfile и это значение предоставляется в качестве параметра методу appendToClassPathForInstrumentation .
Спецификация виртуальной машины Java указывает, что последующая попытка разрешить символическую ссылку, которую виртуальная машина Java ранее неудачно пыталась разрешить, всегда завершается ошибкой с тем же сообщением об ошибке, которое было выброшено в результате первой попытки разрешения. Следовательно, если JAR-файл содержит запись, соответствующую классу, для которого виртуальная машина Java неудачно пыталась разрешить ссылку, то последующие попытки разрешить эту ссылку завершатся той же ошибкой, что и первая попытка.
Этот метод не изменяет значение java.class.path system property.
- Parameters:
-
jarfile- JAR-файл, который будет просматриваться, когда загрузчик системных классов неудачно ищет класс. - Throws:
-
UnsupportedOperationException- Если загрузчик системных классов не поддерживает добавление JAR-файла для поиска. -
NullPointerException- Еслиjarfileравноnull. - Since:
- 1.6
- See Also:
isNativeMethodPrefixSupported
boolean isNativeMethodPrefixSupported()
Can-Set-Native-Method-Prefix манифеста установлен в true в JAR-файле агента (как описано в спецификации пакета) и JVM поддерживает эту функцию. Во время одной инстанциации одной JVM несколько вызовов этого метода всегда возвращают один и тот же ответ.- Returns:
- true, если текущая конфигурация JVM поддерживает установку префикса для метода нативного кода, false — в противном случае.
- Since:
- 1.6
- See Also:
setNativeMethodPrefix
void setNativeMethodPrefix(ClassFileTransformer transformer, String prefix)
ClassFileTransformer это позволяет инструментировать методы нативного кода. Поскольку методы нативного кода не могут быть напрямую инструментированы (у них нет байткодов), их необходимо оборачивать в метод, не являющийся методом нативного кода, который может быть инструментирован. Например, если у нас было:
native boolean foo(int x);
Мы могли бы преобразовать файл класса (с ClassFileTransformer во время первоначального определения класса) таким образом, чтобы это стало:
boolean foo(int x) {
... record entry to foo ...
return wrapped_foo(x);
}
native boolean wrapped_foo(int x); Где foo становится оболочкой для фактического метода нативного кода с добавленным префиксом "wrapped_". Обратите внимание, что "wrapped_" был бы плохим выбором префикса, поскольку он, возможно, мог бы сформировать имя уже существующего метода, поэтому что-то вроде "$$$MyAgentWrapped$$$_" было бы лучше, но сделало бы эти примеры менее читабельными.
Обертка позволит собирать данные о вызове метода нативного кода, но теперь проблема заключается в связывании обернутого метода с реализацией нативного кода. То есть, метод wrapped_foo должен быть разрешен для реализации нативного кода foo, которая может быть:
Java_somePackage_someClass_foo(JNIEnv* env, jint x)
Эта функция позволяет указать префикс и обеспечить правильное разрешение. В частности, при неудачном стандартном разрешении разрешение повторяется, учитывая префикс. Существует два способа разрешения: явное разрешение с функцией JNI RegisterNatives и стандартное автоматическое разрешение. В случае RegisterNatives, JVM попытается установить эту ассоциацию:
method(foo) -> nativeImplementation(foo)
При неудаче разрешение повторяется с указанным префиксом, добавленным в начало имени метода, что приводит к правильному разрешению:
method(wrapped_foo) -> nativeImplementation(foo)
В случае автоматического разрешения JVM попытается:
method(wrapped_foo) -> nativeImplementation(wrapped_foo)
При неудаче разрешение повторяется с удалением указанного префикса из имени реализации, что приводит к правильному разрешению:
method(wrapped_foo) -> nativeImplementation(foo)
Обратите внимание, что поскольку префикс используется только при неудаче стандартного разрешения, методы нативного кода могут быть обернуты выборочно.
Поскольку каждый ClassFileTransformer может выполнять собственное преобразование байткодов, может быть применено более одного слоя оберток. Таким образом, каждый преобразователь нуждается в собственном префиксе. Поскольку преобразования применяются в порядке, префиксы, если они применяются, будут применяться в том же порядке (см. addTransformer). Таким образом, если три преобразователя применили обертки, foo может стать $trans3_$trans2_$trans1_foo. Но если, например, второй преобразователь не применил обертку к foo, она останется просто $trans3_$trans1_foo. Для эффективного определения последовательности префиксов промежуточный префикс применяется только в том случае, если существует обертка, не являющаяся методом нативного кода. Таким образом, в последнем примере, даже если $trans1_foo не является методом нативного кода, префикс $trans1_ применяется, поскольку $trans1_foo существует.
- Parameters:
-
transformer- ClassFileTransformer, который использует этот префикс для обертывания. -
prefix- Префикс, который будет применяться к обернутым методам нативного кода при повторной попытке разрешения неудачного метода нативного кода. Если префикс равен либоnull, либо пустой строке, то неудачные разрешения методов нативного кода для этого преобразователя не будут повторяться. - Throws:
-
NullPointerException- Если переданnullпреобразователь. -
UnsupportedOperationException- Если текущая конфигурация JVM не допускает установку префикса для метода нативного кода (isNativeMethodPrefixSupported()false). -
IllegalArgumentException- Если преобразователь не зарегистрирован (см.addTransformer). - Since:
- 1.6
Переопределение модуля
void redefineModule(Module module, Set<Module> extraReads, Map<String,Set<Module>> extraExports, Map<String,Set<Module>> extraOpens, Set<Class<?>> extraUses, Map<Class<?>,List<Class<?>>> extraProvides)
Этот метод не может уменьшить набор модулей, которые читает модуль, ни уменьшить набор пакетов, которые он экспортирует или открывает, ни уменьшить набор сервисов, которые он использует или предоставляет. Этот метод является бесполезным (no-op), когда вызывается для переопределения безымянного модуля.
При расширении сервисов, которые использует или предоставляет модуль, агент несет ответственность за обеспечение того, что тип сервиса будет доступен в каждом месте инструментирования, где используется тип сервиса. Этот метод не проверяет, является ли тип сервиса членом модуля или содержится в пакете, экспортированном в модуль другим модулем, который он читает.
Параметр extraExports — это карта дополнительных пакетов для экспорта. Параметр extraOpens — это карта дополнительных пакетов для открытия. В обоих случаях, ключ карты — это полное имя пакета, определенное в разделе 6.5.3 спецификации языка Java, например,
"java.lang". Значение карты — это непустой набор модулей, для которых пакет должен быть экспортирован или открыт.
Параметр extraProvides — это дополнительные поставщики сервисов для предоставления модулем. Ключ карты — это тип сервиса. Значение карты — это непустой список типов реализации, каждый из которых является членом модуля и реализацией сервиса.
Этот метод безопасен для одновременного использования и позволяет нескольким агентам инструментировать и обновлять один и тот же модуль примерно в одно и то же время.
- Параметры:
-
module- модуль для переопределения -
extraReads- возможно пустой набор дополнительных модулей для чтения -
extraExports- возможно пустая карта дополнительных пакетов для экспорта -
extraOpens- возможно пустая карта дополнительных пакетов для открытия -
extraUses- возможно пустой набор дополнительных сервисов для использования -
extraProvides- возможно пустая карта дополнительных сервисов для предоставления - Исключения:
-
IllegalArgumentException- ЕслиextraExportsилиextraOpensсодержит ключ, который не является пакетом в модуле; еслиextraExportsилиextraOpensсопоставляет ключ с пустым набором; если значение в картеextraProvidesсодержит тип поставщика сервиса, который не является членом модуля или реализацией сервиса; илиextraProvidesсопоставляет ключ с пустым списком -
UnmodifiableModuleException- если модуль нельзя изменить -
NullPointerException- если любой из аргументовnullили любой из наборов или карт содержитnullключ или значение - С:
- 9
- См. также:
isModifiableModule
boolean isModifiableModule(Module module)
redefineModule. Если модуль изменяем, то этот метод возвращает true. Если модуль неизменяем, то этот метод возвращает false. Этот метод всегда возвращает true когда модуль является безымянным модулем (так как переопределение безымянного модуля является бесполезным действием (no-op)).- Параметры:
-
module- модуль для проверки возможности его изменения - Возвращает:
-
trueесли модуль изменяем, иначеfalse - Исключения:
-
NullPointerException- если модульnull - С:
- 9
© 1993, 2023, Oracle and/or its affiliates. All rights reserved.
Documentation extracted from Debian's OpenJDK Development Kit package.
Licensed under the GNU General Public License, version 2, with the Classpath Exception.
Various third party code in OpenJDK is licensed under different licenses (see Debian package).
Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.
https://docs.oracle.com/en/java/javase/21/docs/api/java.instrument/java/lang/instrument/Instrumentation.html