Интерфейс 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. Эта функция повторно запускает процесс трансформации (независимо от того, происходила ли ранее трансформации). Эта перетрансформация выполняется следующим образом:
- начиная с начального байтового кода файла класса
- для каждого трансформатора, который был добавлен с
canRetransformfalse, байты, возвращённые методомtransformво время последней загрузки или переопределения класса, повторно используются в качестве выходных данных трансформации; обратите внимание, что это эквивалентно повторному применению предыдущей трансформации без изменений; за исключением того, чтоtransformметод не вызывается. - для каждого трансформатора, который был добавлен с
canRetransformtrue, методtransformвызывается в этих трансформаторах - трансформированный байтовый код файла класса устанавливается в качестве нового определения класса
Порядок трансформации описан в ClassFileTransformer. Этот же порядок используется при автоматическом повторном применении трансформаций, неспособных к перетрансформации.
Начальный байтовый код файла класса представляет собой байты, переданные ClassLoader.defineClass или redefineClasses (до применения каких-либо трансформаций), однако они могут не совпадать с ними точно. Пул констант может не иметь той же компоновки или содержимого. Пул констант может содержать больше или меньше элементов. Элементы пула констант могут быть в другом порядке; однако индексы пула констант в байкодах методов будут соответствовать. Некоторые атрибуты могут отсутствовать. Там, где порядок не имеет значения, например, порядок методов, порядок может не сохраняться.
Этот метод работает с набором, чтобы позволить взаимозависимые изменения более чем одного класса одновременно (перетрансформация класса A может потребовать перетрансформации класса B).
Если у перетрансформированного метода есть активные кадры стека, эти активные кадры продолжают выполнять байткоды оригинального метода. Перетрансформированный метод будет использоваться при новых вызовах.
Этот метод не вызывает инициализацию, кроме той, которая происходит по стандартной семантике 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. Байты файла класса не проверяются, не верифицируются и не устанавливаются до тех пор, пока не будут применены преобразования, если полученные байты неверны, этот метод выбросит исключение.
Если этот метод выбросит исключение, ни один класс не был переопределён.
Этот метод предназначен для использования в инструментировании, как описано в спецификации класса.
- Параметры:
-
definitions- массив классов для переопределения с соответствующими определениями; разрешён массив нулевой длины, в этом случае этот метод ничего не делает - Исключения:
-
UnmodifiableClassException- если указанный класс не может быть изменён (isModifiableClass(java.lang.Class<?>)вернётfalse) -
UnsupportedOperationException- если текущая конфигурация JVM не позволяет переопределять (isRedefineClassesSupported()ложь) или переопределение пыталось произвести неподдерживаемые изменения -
ClassFormatError- если данные не содержали допустимого класса -
NoClassDefFoundError- если имя в файле класса не равно имени класса -
UnsupportedClassVersionError- если номера версий файлов класса не поддерживаются -
ClassCircularityError- если новые классы содержат цикличность -
LinkageError- если произошла ошибка связи -
NullPointerException- если предоставленный массив определений или любой из его компонентовnull -
ClassNotFoundException- Не может быть выброшен (присутствует только для совместимости) - См. также:
isModifiableClass
boolean isModifiableClass(Class<?> theClass)
true. Если класс нельзя изменить, этот метод возвращает false.
Для повторной трансформации класса также должно быть истинно isRetransformClassesSupported(). Но значение isRetransformClassesSupported() не влияет на значение, возвращаемое этой функцией. Для переопределения класса также должно быть истинно isRedefineClassesSupported(). Но значение isRedefineClassesSupported() не влияет на значение, возвращаемое этой функцией.
Примитивные классы (например, java.lang.Integer.TYPE) и массивы классов никогда не могут быть изменены.
- Параметры:
-
theClass- класс, который нужно проверить на возможность изменения - Возвращает:
- можно ли изменить указанный класс
- Исключения:
-
NullPointerException- если указанный классnull. - С тех пор:
- 1.6
- См. также:
getAllLoadedClasses
Class[] getAllLoadedClasses()
- Возвращает:
- массив, содержащий все классы, загруженные JVM; массив нулевой длины, если таких нет
getInitiatedClasses
Class[] getInitiatedClasses(ClassLoader loader)
loader может найти по имени с помощью ClassLoader::loadClass, Class::forName и связывания байткода. То есть все классы, для которых loader был записан как инициализирующий загрузчик. Если указанный loader является null, возвращаются классы, которые может найти загрузчик bootstrap по имени.
Возвращаемый массив не включает скрытые классы или интерфейсы или классы массивов, чья элементный тип является скрытым классом или интерфейсом, так как их нельзя обнаружить ни одним загрузчиком классов.
- Параметры:
-
loader- загрузчик, чьи начальные классы будут возвращены - Возвращает:
- массив, содержащий все классы, которые
loaderможет найти по имени; массив нулевой длины, если таких нет
getObjectSize
long getObjectSize(Object objectToSize)
- Параметры:
-
objectToSize- объект для определения размера - Возвращает:
- специфичное для реализации приблизительное значение занимаемого объектом хранилища
- Исключения:
-
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 безуспешно пыталась разрешить ссылку, последующие попытки разрешить эту ссылку завершатся той же ошибкой, что и первоначальная попытка.
- Параметры:
-
jarfile- JAR-файл, который будет проверяться при безуспешном поиске класса загрузчиком bootstrap. - Исключения:
-
NullPointerException- еслиjarfileявляетсяnull. - С тех пор:
- 1.6
- См. также:
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)
Этот метод не может уменьшить набор модулей, которые читает модуль, ни уменьшить набор пакетов, которые он экспортирует или открывает, ни уменьшить набор сервисов, которые он использует или предоставляет. Этот метод является бесполезным, когда вызывается для переопределения безымянного модуля.
При расширении сервисов, которые использует или предоставляет модуль, агент несёт ответственность за обеспечение того, что тип сервиса будет доступен в каждом месте внедрения, где тип сервиса используется. Этот метод не проверяет, является ли тип сервиса членом модуля или пакетом, экспортированным в модуль другим модулем, который он читает.
Параметр 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 когда модуль безымянный (поскольку переопределение безымянного модуля является бесполезным действием).- Параметры:
-
module- модуль для проверки возможности изменения - Возвращает:
-
trueесли модуль изменяем, в противном случаеfalse - Исключения:
-
NullPointerException- если модульnull - С:
- 9
© 1993, 2021, 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/17/docs/api/java.instrument/java/lang/instrument/Instrumentation.html