Интерфейс 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(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.
- См. также:
redefineClasses
void redefineClasses(ClassDefinition... definitions) throws ClassNotFoundException, UnmodifiableClassException
Этот метод используется для замены определения класса без обращения к существующим байтам файла класса, например при повторной компиляции исходного кода для отладки с возможностью продолжения работы. Если требуется преобразовать существующие байты файла класса (например, при инструментировании байт-кода), следует использовать retransformClasses.
Этот метод работает с набором классов, чтобы позволить одновременно вносить взаимозависимые изменения в несколько классов (переопределение класса A может потребовать переопределения класса B).
Если у переопределённого метода есть активные кадры стека, эти кадры продолжат выполнять байт-код исходного метода. При новых вызовах будет использоваться переопределённый метод.
Этот метод не вызывает никакой инициализации, кроме той, которая происходит в соответствии с обычной семантикой JVM. Иными словами, переопределение класса не приводит к запуску его инициализаторов. Значения статических переменных останутся такими, какими они были до вызова.
Экземпляры переопределённого класса не затрагиваются.
Поддерживаемые изменения файлов классов описаны в разделе JVM TI RedefineClasses. Проверка и установка байтов файла класса выполняются только после применения преобразований; если полученные байты содержат ошибки, этот метод выбрасывает исключение.
Если этот метод выбрасывает исключение, ни один класс не был переопределён.
Этот метод предназначен для использования при инструментировании, как описано в спецификации класса.
- Параметры:
-
definitions— массив классов для переопределения с соответствующими определениями; допускается массив нулевой длины, в этом случае метод ничего не делает - Исключения:
-
UnmodifiableClassException— если указанный класс нельзя изменить (isModifiableClass(Class)вернул быfalse) -
UnsupportedOperationException— если текущая конфигурация JVM не допускает переопределение (isRedefineClassesSupported()равно false) или при переопределении была предпринята попытка внести неподдерживаемые изменения -
ClassFormatError— если данные не содержат допустимый класс -
NoClassDefFoundError— если имя в файле класса не совпадает с именем класса -
UnsupportedClassVersionError— если номера версий файла класса не поддерживаются -
ClassCircularityError— если новые классы образуют цикл -
LinkageError— если возникает ошибка связывания -
NullPointerException— если переданный массив определений или любой его элемент равенnull -
ClassNotFoundException— никогда не выбрасывается (указано только для обеспечения совместимости) - См. также:
isModifiableClass
boolean isModifiableClass(Class<?> theClass)
true. Если класс нельзя изменить, этот метод возвращает false. Чтобы класс можно было повторно преобразовать, значение isRetransformClassesSupported() также должно быть равно true. Однако значение isRetransformClassesSupported() не влияет на результат, возвращаемый этой функцией. Чтобы класс можно было переопределить, значение isRedefineClassesSupported() также должно быть равно true. Однако значение 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, возвращаются классы, которые загрузчик классов начальной загрузки может найти по имени. Возвращённый массив не включает скрытые классы и интерфейсы или классы массивов, у которых тип элемента является скрытым классом или интерфейсом, поскольку ни один загрузчик классов не может обнаружить их.
- Параметры:
-
loader— загрузчик, список инициированных классов которого будет возвращён - Возвращает:
- массив, содержащий все классы, которые
loaderможет найти по имени; если таких классов нет, возвращается массив нулевой длины
getObjectSize
long getObjectSize(Object objectToSize)
- Параметры:
-
objectToSize— объект, размер которого нужно определить - Возвращает:
- зависящую от реализации приблизительную оценку объёма памяти, занимаемого указанным объектом
- Исключения:
-
NullPointerException— если переданный объект равенnull.
appendToBootstrapClassLoaderSearch
void appendToBootstrapClassLoaderSearch(JarFile jarfile)
Если встроенный загрузчик классов виртуальной машины, известный как «загрузчик классов начальной загрузки», не может найти класс, также выполняется поиск среди записей в JAR file.
Этот метод можно использовать несколько раз, чтобы добавить несколько JAR-файлов; поиск в них выполняется в порядке вызова метода.
Агент должен проследить за тем, чтобы JAR-файл не содержал классов или ресурсов, кроме тех, которые должны определяться загрузчиком классов начальной загрузки для инструментирования. Несоблюдение этого предупреждения может привести к неожиданному поведению, которое трудно диагностировать. Например, предположим, что существует загрузчик L, родительским загрузчиком которого при делегировании является загрузчик классов начальной загрузки. Кроме того, метод класса C, определённого загрузчиком L, ссылается на непубличный класс доступа C$1. Если JAR-файл содержит класс C$1, делегирование загрузчику классов начальной загрузки приведёт к тому, что C$1 будет определён этим загрузчиком. В этом примере будет выброшено исключение IllegalAccessError, которое может привести к сбою приложения. Один из способов избежать подобных проблем — использовать для классов инструментирования уникальное имя пакета.
В Спецификации виртуальной машины Java указано, что последующая попытка разрешить символическую ссылку, которую виртуальная машина Java ранее безуспешно пыталась разрешить, всегда завершается с той же ошибкой, которая была выброшена при первой попытке разрешения. Следовательно, если JAR-файл содержит запись, соответствующую классу, ссылку на который виртуальная машина Java ранее безуспешно пыталась разрешить, последующие попытки разрешить эту ссылку завершатся с той же ошибкой, что и первая попытка.
- Параметры:
-
jarfile— JAR-файл, в котором выполняется поиск, если загрузчику классов начальной загрузки не удалось найти класс. - Исключения:
-
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.
- Параметры:
-
jarfile— JAR-файл, в котором выполняется поиск, если системному загрузчику классов не удалось найти класс. - Исключения:
-
UnsupportedOperationException— если системный загрузчик классов не поддерживает добавление JAR-файла для поиска. -
NullPointerException— еслиjarfileравенnull. - Начиная с версии:
- 1.6
- См. также:
isNativeMethodPrefixSupported
boolean isNativeMethodPrefixSupported()
Can-Set-Native-Method-Prefix имеет значение true в JAR-файле агента (как описано в спецификации пакета) и JVM поддерживает эту возможность. В течение одного экземпляра JVM все вызовы этого метода всегда возвращают один и тот же результат.- Возвращает:
- true, если текущая конфигурация JVM поддерживает установку префикса для нативных методов; в противном случае false.
- Начиная с версии:
- 1.6
- См. также:
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_ применяется, хотя $trans1_foo не является нативным методом, поскольку существует $trans1_foo.
- Параметры:
-
transformer— ClassFileTransformer, который добавляет оболочку с использованием этого префикса. -
prefix— префикс, добавляемый к обёрнутым нативным методам при повторной попытке разрешения нативного метода после сбоя. Если префикс равенnullили представляет собой пустую строку, повторные попытки разрешения нативных методов после сбоя для этого преобразователя не выполняются. - Исключения:
-
NullPointerException— если переданnullпреобразователь. -
UnsupportedOperationException— если текущая конфигурация JVM не допускает установку префикса для нативных методов (isNativeMethodPrefixSupported()равно false). -
IllegalArgumentException— если преобразователь не зарегистрирован (см.addTransformer). - Начиная с версии:
- 1.6
redefineModule
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, 2025, 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/25/docs/api/java.instrument/java/lang/instrument/Instrumentation.html