Интерфейс 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 (до применения каких-либо преобразований), но они могут не совпадать точно. Пул констант может иметь другую структуру или содержимое. Пул констант может содержать больше или меньше записей. Записи пула констант могут быть в другом порядке; однако, индексы пула констант в байткодах методов будут соответствовать. Некоторые атрибуты могут отсутствовать. В местах, где порядок не имеет значения, например, порядок методов, порядок может не сохраняться.
Этот метод работает с набором, чтобы разрешить взаимозависимые изменения более чем одного класса одновременно (повторное преобразование класса А может потребовать повторного преобразования класса 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
Этот метод используется для замены определения класса без ссылки на существующие байты файла класса, как это может быть сделано при повторной компиляции из исходного кода для отладки "fix-and-continue". В тех случаях, когда существующие байты файла класса должны быть преобразованы (например, в инструментировании байткода) 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 class loader", безуспешно ищет класс, записи в 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.
- Параметры:
-
jarfile- JAR-файл, который будет проверяться, когда загрузчик системного класса неуспешно ищет класс. - Исключения:
-
UnsupportedOperationException- Если загрузчик системного класса не поддерживает добавление JAR-файла для проверки. -
NullPointerException- Еслиjarfileявляетсяnull. - См.:
isNativeMethodPrefixSupported
boolean isNativeMethodPrefixSupported()
Can-Set-Native-Method-Prefix манифеста установлен в true в JAR-файле агента (как описано в спецификации пакета) и JVM поддерживает эту возможность. Во время одного экземпляра одной JVM многократные вызовы этого метода всегда возвращают один и тот же ответ.- Возвращает:
- true, если текущая конфигурация JVM поддерживает установку префикса для методов нативного кода, false — в противном случае.
- См.:
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 существует.
- Параметры:
-
transformer- ClassFileTransformer, который использует этот префикс для обёрток. -
prefix- Префикс, который необходимо добавить к обернутым методам нативного кода при повторной попытке разрешения неудачного метода нативного кода. Если префикс равен либоnull, либо пустой строке, то повторные попытки разрешения методов нативного кода для этого трансформера не выполняются. - Исключения:
-
NullPointerException- если переданnullтрансформер. -
UnsupportedOperationException- если текущая конфигурация JVM не позволяет установить префикс для метода нативного кода (isNativeMethodPrefixSupported()ложно). -
IllegalArgumentException- если трансформер не зарегистрирован (см.addTransformer).
Переопределить модуль
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://download.java.net/java/early_access/jdk24/docs/api/java.instrument/java/lang/instrument/Instrumentation.html