Схема Содержание
Эта статья представляет собой некоторый опыт и итог изучения Arthas и jvm-sandbox, я надеюсь, что она поможет всем. В этой книге много текстов, которые можно прочитать по оглавлению.
- Предыстория: В чем проблема сейчас?
- Артас: Что Артас может сделать для тебя? Каковы принципы различных команд?
- jvm-sandbox: Как вам может помочь jvm-sandbox?
- Принцип реализации? Как реализовать самому?
- Некоторые общие вопросы?
1. Предпосылки
Прошел 2018 год, но в прошлом году было много отличных проектов с открытым исходным кодом.Здесь я хотел бы представить два относительно похожих проекта с открытым исходным кодом Alibaba, один — Arthas, а другой — jvm-sandbox. Эти два проекта открыты в этом году, зачем представлять эти два проекта? Давайте сначала продадим его здесь, давайте спросим, встречались ли вы со следующей сценой?
- Когда возникает проблема с вашим онлайн-проектом, но при открытии лога вы обнаруживаете, что забыли зайти в лог в некоторых местах, поэтому тут же составляете лог и снова выходите в интернет. Это относительно легко для некоторых компаний с нерегулярными онлайн-процедурами, некоторые процедуры относительно строгие, например, когда Meituan выходит в онлайн, есть период бана, и его можно запустить только после 9 часов. Не исключено, что такая задержка отсрочит золотой момент решения проблемы.
- Когда интерфейс в вашем проекте выполняется медленно, чтобы устранить проблему, вы везде добавляете время выполнения каждого метода.
- Когда вы обнаружите конфликт классов, кажется, что результат онлайн-операции не соответствует вашим ожиданиям, вручную загрузите файл класса, скомпилированный онлайн, а затем декомпилируйте его, чтобы увидеть, каково содержимое класса.
- Когда код готов к совместной отладке, но нижестоящая бизнес-среда не готова, вы по очереди аннотируете предыдущий код и снова пишете его в виде макета, чтобы облегчить совместную отладку.
Вышеуказанные сценарии более или менее встречались в реальном развитии бизнеса, и в целом то, как все с ними справляются, примерно такое же, как то, что я описал в сценарии. И здесь я хотел бы представить Arthas и jvm-sandbox.Если вы изучите эти два проекта, все вышеперечисленные проблемы больше не будут для вас сложными.
2. Arthas
Конечно, прежде чем представить Arthas, я все же должен рассказать вам о Greys.И Arthas, и jvm-sandbox произошли от Greys.Это онлайн-инструмент диагностики проблем Java, исходный код которого был открыт Alibaba в 2014 году. Arthas можно рассматривать как его обновленную версию, которая является лучшим и более многофункциональным инструментом диагностики Java. Представленный в его github READEME этот инструмент может помочь вам сделать следующее:
- Из какого пакета jar загружается этот класс? Почему сообщается о различных исключениях, связанных с классом?
- Почему код, который я изменил, не выполняется? Это потому, что я не совершал? Ветвь неправильная?
- Если вы столкнулись с проблемой и не можете отладить ее онлайн, можете ли вы опубликовать ее повторно, только добавив журнал?
- Есть проблема с обработкой данных пользователя онлайн, но ее нельзя отладить онлайн или воспроизвести оффлайн!
- Есть ли глобальный вид, чтобы увидеть работоспособность системы?
- Есть ли способ отслеживать рабочее состояние JVM в реальном времени?
Ниже я представлю некоторые общие команды и способы использования Arthas, чтобы увидеть, как решать наши актуальные проблемы.Что касается руководства по установке, пожалуйста, обратитесь к github Arthas.
2.1 Странные ошибки загрузки класса
С ошибкой NoSuchMethodError, думаю, сталкивался каждый.Как правило, первая реакция старых водителей, когда они видят эту ошибку, это конфликт номера версии jar-пакета.Вообще говоря, такого рода проблемы легко решаются с помощью какого-нибудь плагина. -ins maven.
Я уже сталкивался со странной проблемой.У нас есть два клиент-jar пакета для сервисов, и имя пакета и имя класса одного класса совпадают.При написании кода я не заметил этой проблемы.На этапе компиляции из-за имя пакета и имя класса все одинаковы, на всех этапах компиляции не сообщается об ошибках, и нет проблем на этапе автономного выполнения, но на этапе выполнения машины в тестовой среде проблема отсутствует. Это немного отличается от предыдущего конфликта номера версии пакета jar, потому что мы хотим использовать этот класс пакета client-jar службы A во время расследования, но номер версии этого пакета jar действительно уникален в Maven.
В это время Артас может проявить свою магию.
2.1.1 команда sc
Найдите соответствующий класс, а затем выведите следующую команду (вариант использования использует официально предоставленный вариант использования):
$ sc -d demo.MathGame
class-info demo.MathGame
code-source /private/tmp/arthas-demo.jar
name demo.MathGame
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name MathGame
modifier public
annotation
interfaces
super-class +-java.lang.Object
class-loader +-sun.misc.Launcher$AppClassLoader@3d4eac69
+-sun.misc.Launcher$ExtClassLoader@66350f69
classLoaderHash 3d4eac69
Affect(row-cnt:1) cost in 875 ms.
Вы можете видеть, что исходный код распечатывается.В то время было обнаружено, что исходный код не был взят из соответствующего пакета Jar, поэтому было обнаружено, что два сервиса использовали одно и то же имя пакета и имя класса для тот же класс, что приводит к этой странной Проблема решается путем изменения имени пакета и имени класса позже.
принцип sc
Информация sc в основном получена из соответствующего класса. Например, isInterface, isAnnotation и т.п. получаются следующими способами:
Какой пакет jar загружается для одного из наших вышеуказанных классов, получается через CodeSource:2.1.2 jad
Артас также предоставляет команду jad для декомпиляции, которая очень полезна для разрешения ошибок конфликта классов.Например, если мы хотим узнать, что такое код в этом классе, мы можем сделать это напрямую с помощью команды jad:
$ jad java.lang.String
ClassLoader:
Location:
/*
* Decompiled with CFR 0_132.
*/
package java.lang;
import java.io.ObjectStreamField;
...
public final class String
implements Serializable,
Comparable<String>,
CharSequence {
private final char[] value;
private int hash;
private static final long serialVersionUID = -6849794470754667710L;
private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator();
public String(byte[] arrby, int n, int n2) {
String.checkBounds(arrby, n, n2);
this.value = StringCoding.decode(arrby, n, n2);
}
...
Как правило, с помощью этой команды мы можем узнать, отсутствуют ли некоторые методы в ожидаемом вами классе или некоторые методы изменились, чтобы определить конфликт пакета jar.
джад принцип
jad использует для декомпиляции пакет jar, предоставленный cfr. Здесь процесс более сложный и здесь описываться не будет.
2.2 Динамическое изменение уровня журнала
Многие студенты могут подумать, какая польза от журнала динамических изменений? Кажется, вы еще не использовали его? Вообще говоря, могут потребоваться следующие сценарии:
- Как правило, уровень журнала по умолчанию — информация.Иногда вам нужно просмотреть журнал отладки, и вам может потребоваться снова подключиться к сети.
- Когда есть большой объем трафика онлайн-приложений, если есть проблемы с бизнесом, за короткий промежуток времени может быть сгенерировано большое количество журналов, так как журнал будет записываться на диск, он будет потреблять много памяти и дискового ввода-вывода, чтобы еще больше усугубить серьезность нашей проблемы, а затем вызвать лавину. Мы можем использовать журнал динамических изменений для решения двух вышеуказанных проблем.Мы разработали инструмент в Meituan, чтобы использовать LogContext для записи всех logConfig, а затем динамически изменять и обновлять. Но как мы можем динамически изменять журнал без этого инструмента?
2.2.1 ognl
Ognl - это язык выражения. В Arthas вы можете использовать этот язык выражения, чтобы делать много вещей, таких как выполнение метода и получения определенной информации. Здесь мы можем динамически изменять уровень журнала со следующей командой:
$ ognl '@com.lz.test@LOGGER.logger.privateConfig'
@PrivateConfig[
loggerConfig=@LoggerConfig[root],
loggerConfigLevel=@Level[INFO],
intLevel=@Integer[400],
]
$ ognl '@com.lz.test@LOGGER.logger.setLevel(@org.apache.logging.log4j.Level@ERROR)'
null
$ ognl '@com.lz.test@LOGGER.logger.privateConfig'
@PrivateConfig[
loggerConfig=@LoggerConfig[root],
loggerConfigLevel=@Level[ERROR],
intLevel=@Integer[200],
]
Приведенная выше команда может изменить информационный журнал соответствующего уровня класса для печати журнала ошибок, если вы хотите глобально изменить корневой уровень, то выполнить его сложнее для выражения ognl, в общем случае необходимо перевести в ognl следующий код:
org.apache.logging.log4j.core.LoggerContext loggerContext = (org.apache.logging.log4j.core.LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
Map<String, LoggerConfig> map = loggerContext.getConfiguration().getLoggers();
for (org.apache.logging.log4j.core.config.LoggerConfig loggerConfig : map.values()) {
String key = loggerConfig.getName();
if (StringUtils.isBlank(key)) {
loggerConfig.setLevel(Level.ERROR);
}
}
loggerContext.updateLoggers();
Вообще говоря, это сложнее, и реализация здесь не приводится.Если вам интересно, вы можете реализовать следующее в виде кода.Так же реализован компонент журнала динамической настройки Meituan.
принцип
Конкретный принцип заключается в том, чтобы сначала получить AppClassLoader (по умолчанию) или указанный ClassLoader, а затем вызвать пакет Ognl для автоматического выполнения и анализа выражения, и выполненный класс будет получен из предыдущего ClassLoader.
2.3 Как узнать, вызывается ли метод
Много раз выполнение нашего метода не соответствовало нашим ожиданиям, но мы не знаем, где оно не соответствует Команда часов Артаса может помочь нам решить эту проблему.
2.3.1 watch
Команда watch наблюдает, как следует из ее названия, она может наблюдать за вызовом указанного метода и определяет 4 точки события наблюдения: до вызова метода -b, после того, как метод -e является ненормальным, после возврата метода -s и после завершения метода -f. По умолчанию -f
Например, мы хотим знать, каковы параметры и возвращаемые значения при выполнении метода. Обратите внимание, что параметры здесь являются параметрами при выполнении метода и могут меняться в зависимости от входных параметров.
$ watch demo.MathGame primeFactors "{params,returnObj}" -x 2
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 44 ms.
ts=2018-12-03 19:16:51; [cost=1.280502ms] result=@ArrayList[
@Object[][
@Integer[535629513],
],
@ArrayList[
@Integer[3],
@Integer[19],
@Integer[191],
@Integer[49199],
],
]
Вы можете получить информацию о параметрах и возвращаемых значениях, а также о потреблении времени метода.
принцип
Используйте инструмент jdk1.6 + ASM для записи входных и выходных параметров метода, а также времени потребления метода.
2.4 Как узнать, требует ли метод больше времени
Когда метод занимает много времени, в это время вам нужно проверить, не заняло ли определенное место много времени.Как правило, такого рода проблемы сложнее проверить, и они проверяются путем отслеживания графа трассировки всей ссылки. .В локальном приложении нет графа трассировки.В настоящее время для устранения проблемы необходима команда трассировки Arthas.
2.4.1 trace
Команда трассировки может активно искать путь вызова метода, соответствующий шаблону класса/шаблону метода, отображать и подсчитывать все издержки производительности и отслеживать цепочку вызовов по всей цепочке вызовов.
Но призывы трассировки могут отслеживать только уровень ссылок, если информация об уровне ссылки недостаточно, вы можете снова установить ссылку на проблемные методы трассировки. трассировка, используя следующие примеры.
$ trace demo.MathGame run
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 42 ms.
`---ts=2018-12-04 00:44:17;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@3d4eac69
`---[10.611029ms] demo.MathGame:run()
+---[0.05638ms] java.util.Random:nextInt()
+---[10.036885ms] demo.MathGame:primeFactors()
`---[0.170316ms] demo.MathGame:print()
Видно, что самый трудоемкий метод выше — это PrimeFactors, поэтому мы можем отследить его для дальнейшего исследования.
принцип
Используйте инструмент jdk1.6 + ASM. Регистрация происходит до и после метода доступа.
2.5 Как использовать команду для повторной отправки запроса?
Иногда для устранения неполадок требуется, чтобы восходящий поток снова вызывал этот метод, например, с помощью таких инструментов, как postMan.Конечно, Артас предоставляет команду, чтобы заменить нас ручными запросами туда и обратно.
2.5.1 tt
Официальное введение tt: пространственно-временной туннель данных выполнения метода записывает входные параметры и возвращаемую информацию о каждом вызове указанного метода и может наблюдать за этими вызовами в разное время. Видно, что tt можно использовать для записи запросов, ну и конечно же он поддерживает наш реплей. Если вы хотите записать метод, вы можете использовать следующую команду:
$ tt -t demo.MathGame primeFactors
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 66 ms.
INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD
-------------------------------------------------------------------------------------------------------------------------------------
1000 2018-12-04 11:15:38 1.096236 false true 0x4b67cf4d MathGame primeFactors
1001 2018-12-04 11:15:39 0.191848 false true 0x4b67cf4d MathGame primeFactors
1002 2018-12-04 11:15:40 0.069523 false true 0x4b67cf4d MathGame primeFactors
1003 2018-12-04 11:15:41 0.186073 false true 0x4b67cf4d MathGame primeFactors
1004 2018-12-04 11:15:42 17.76437 true false 0x4b67cf4d MathGame primeFactors
Вышеупомянутые записанные 5 сцен среды вызова, которые также можно рассматривать как запись 5 информации о запросе возврата. Например, если мы хотим выбрать для воспроизведения запросы с индексом 1004, мы можем ввести следующую команду.
$ tt -i 1004 -p
RE-INDEX 1004
GMT-REPLAY 2018-12-04 11:26:00
OBJECT 0x4b67cf4d
CLASS demo.MathGame
METHOD primeFactors
PARAMETERS[0] @Integer[946738738]
IS-RETURN true
IS-EXCEPTION false
RETURN-OBJ @ArrayList[
@Integer[2],
@Integer[11],
@Integer[17],
@Integer[2531387],
]
Time fragment[1004] successfully replayed.
Affect(row-cnt:1) cost in 14 ms.
Обратите внимание, что запрос на воспроизведение требует двух вещей:
-
Потеря информации ThreadLocal: поскольку используется вызов потока Arthas, информация threadLocal будет потеряна, например, может быть потеряна некоторая информация TraceId.
-
Ссылочный объект: сохраненный входной параметр является сохраненной ссылкой, а не копией, поэтому, если содержимое параметра изменяется, входной параметр фактически изменяется.
2.6 Часто срабатывают некоторые трудоемкие методы, как узнать, кто их вызывал?
Иногда некоторые методы требуют очень много времени или очень важны, и вам нужно знать, кто инициировал вызов, например System.gc().Иногда, если вы обнаружите, что fullgc часто вызывается System.gc(), вам нужно проверить как называется это приложение, вы можете использовать следующую команду.
2.6.1
Мы можем ввести следующую команду:
$ options unsafe true
NAME BEFORE-VALUE AFTER-VALUE
-----------------------------------
unsafe false true
$ stack java.lang.System gc
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 50 ms.
ts=2019-01-20 21:14:05;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@14dad5dc
@java.lang.System.gc()
at com.lz.test.Test.main(Test.java:322)
Первые параметры ввода unsafe true позволяют нам улучшить jdk, затем System.gc будет отслеживать и записывать текущий стек вызовов, чтобы получить позицию.
2.7 Как переопределить класс?
Иногда мы находим все команды и обнаруживаем, что они не соответствуют нашим потребностям, тогда мы можем в это время переопределить этот класс, и мы можем использовать следующие команды.
2.7.1 redefine
Команда redefine предусматривает, что мы можем переопределить класс в jvm, но класс не может быть восстановлен после использования этой команды. Сначала нам нужно скомпилировать переписанный класс, загрузить его в указанную нами директорию и выполнить следующие операции:
redefine -p /tmp/Test.class
Мы можем переопределить наш Test.class. Тем самым изменив логику для выполнения наших пользовательских требований.
2.8 Краткое изложение Артаса
Выше представлены 7 распространенных сценариев и команд Артаса. Конечно, эта команда намного больше, и использование каждой команды не ограничивается тем, что я представил. Особенно после открытого исходного кода в нем участвовало больше разработчиков, и теперь он был оптимизирован для интерфейса, который можно использовать для устранения неполадок в Интернете, чтобы решить различные неудобства, связанные с онлайн-установкой.
Больше команд можно найти в документации пользователя Arthas:Alibaba.GitHub.IO/искусство имеет/в…
3.jvm-sandbox
Мощный Arthas был представлен вам выше.Многие также хотят сделать инструмент, который может динамически заменять Class, но такого рода вещи слишком низкоуровневы, относительно малы, а порог для входа относительно высок. Но jvm-sandbox предоставляет нам простой для понимания метод кодирования для динамической замены Class.
3.1 AOP
Вы не должны быть новичком в АОП Мы можем легко реализовать АОП в Spring, но у этого есть два недостатка: во-первых, его можно улучшить только для bean-компонентов в Spring, а во-вторых, после улучшения, если вы хотите. вы изменяете расширенный контент, вы можете только переписать, а затем опубликовать проект, а не динамическое улучшение.
3.2 Что может принести санбокс
JVM Sandbox использует технологию HotSwap для достижения этого без перезапуска JVM:
- Улучшение АОП для любого метода в любом классе в JVM во время выполнения
- Модули расширения могут быть динамически горячими
- Измените поток выполнения любого метода через модули расширения
То есть мы можем использовать эту технику для выполнения наших команд в артхасе. Вообще говоря, применимые сценарии песочницы следующие:
- Онлайн-локация неисправности
- Онлайн-контроль потока системы
- Онлайн-симуляция неисправности
- Запись запроса метода и воспроизведение результата
- Динамическая печать журнала
- ...
Конечно, сценариев больше, то, что он может сделать, полностью зависит от вашего воображения, пока вы можете это придумать, он может это сделать.
3.3 Пример песочницы
Песочница предоставляет концепцию модуля, каждый модуль является экземпляром АОП. Например, если мы хотим завершить модуль, который печатает все журналы sql операторов jdbc, нам нужно построить следующий модуль:
public class JdbcLoggerModule implements Module, LoadCompleted {
private final Logger smLogger = LoggerFactory.getLogger("DEBUG-JDBC-LOGGER");
@Resource
private ModuleEventWatcher moduleEventWatcher;
@Override
public void loadCompleted() {
monitorJavaSqlStatement();
}
// 监控java.sql.Statement的所有实现类
private void monitorJavaSqlStatement() {
new EventWatchBuilder(moduleEventWatcher)
.onClass(Statement.class).includeSubClasses()
.onBehavior("execute*")
/**/.withParameterTypes(String.class)
/**/.withParameterTypes(String.class, int.class)
/**/.withParameterTypes(String.class, int[].class)
/**/.withParameterTypes(String.class, String[].class)
.onWatch(new AdviceListener() {
private final String MARK_STATEMENT_EXECUTE = "MARK_STATEMENT_EXECUTE";
private final String PREFIX = "STMT";
@Override
public void before(Advice advice) {
advice.attach(System.currentTimeMillis(), MARK_STATEMENT_EXECUTE);
}
@Override
public void afterReturning(Advice advice) {
if (advice.hasMark(MARK_STATEMENT_EXECUTE)) {
final long costMs = System.currentTimeMillis() - (Long) advice.attachment();
final String sql = advice.getParameterArray()[0].toString();
logSql(PREFIX, sql, costMs, true, null);
}
}
....
});
}
}
monitorJavaSqlStatement — наш основной метод. Процесс выглядит следующим образом:
- Во-первых, создайте конструктор наблюдателя событий через новый EventWatchBuilder (moduleEventWatcher).Через Builder мы можем легко создать наш наблюдатель.
- OnClass - отфильтровывать классы, которые нам нужно наблюдать, а входныеClasses содержит все подклассы.
- withParameterTypes дополнительно фильтрует параметры.
- onWatch наблюдает и принимает режим шаблона, который очень похож на наш Spring AOP.Сначала текущее время записывается в before, а затем время before извлекается в afterReturning, чтобы получить текущее время потребления, затем текущий оператор sql получено, и, наконец, для печати.
3.4 Обзор песочницы
Arthas — отличный онлайн-инструмент для диагностики проблем с Java. . Если Артас — это острый меч, способный убить тысячи врагов, то jvm-sandbox — это модель для создания хорошего меча, ожидающая, пока каждый создаст собственный несравненный меч.
Садбокс менее представлен, и заинтересованные студенты могут посетить github, чтобы узнать о нем:GitHub.com/Alibaba/JVM…
4. В качестве альтернативы собственный динамический байт-код
Независимо от нашего Артаса или нашей JVM-песочницы, это следующая техника:
- ASM
- Инструментарий (основной)
- VirtualMachine
4.1 ASM
Чтобы узнать о методах модификации байт-кода ASM, вы можете обратиться к нескольким статьям, которые я написал ранее:
- Байт-код тоже может делать интересные вещи
- Байт-код тоже может делать интересные вещи — ASM
- Научит вас использовать байт-код Java в качестве инструмента десенсибилизации логов.
Для ASM методики модификации байт-кода изложенные здесь не являются излишними.
4.2 Instrumentation
Приборы - это класс, используемый JDK1.6 для сборки кода Java. Приборы добавляют Bytecode к способу для сбора данных или изменить процесс. Конечно, он также предоставляет некоторые дополнительные функции, такие как получение всех загруженных классов в текущем JVM и т. Д.
4.2.1 Получение инструментария
Java предоставляет два метода для получения инструментария, следующие два представляют:
4.2.1.1 premain
При запуске вызывается метод preMain:
public static void premain(String agentArgs, Instrumentation inst) {
}
Нужно добавить дополнительную команду при запуске
java -javaagent:jar 文件的位置 [= 传入 premain 的参数 ]
PreMainClass также необходимо настроить в maven.
существуетНаучит вас использовать байт-код Java в качестве инструмента десенсибилизации логов.Премейн подробно описан в
4.2.1.2 agentmain
Premain — это прокси-метод, предоставляемый Java SE5, который преподнес разработчикам множество сюрпризов, но некоторые должны остаться без изменений, потому что в командной строке необходимо указать jar-прокси, а прокси-класс должен запускаться перед основным методом. Поэтому разработчикам необходимо подтвердить логику обработки и содержание параметров агента перед применением, что в некоторых случаях сложно. Например, в обычной производственной среде функция агента обычно не включена.В конце концов, java SE6 предоставляет нам agentmain для динамического изменения без установки агента. В документации по JavaSE6 разработчики могут не увидеть четкого введения в документации, относящейся к пакету java.lang.instrument, не говоря уже о конкретном примере применения agnetmain. Однако в новых функциях Java SE 6 есть незаметное место, раскрывающее использование agentmain. Это API-интерфейс Attach, предоставляемый в Java SE 6.
Attach API — это не стандартный API Java, а набор расширений API, предоставляемых корпорацией Sun для «присоединения» (присоединения) программы инструмента агента к целевой JVM. С его помощью разработчики могут легко контролировать JVM и запускать дополнительный агент.
Предоставляет интерфейс для подключения в VirtualMachine.
4.3 Реализация горячей замены
Все коды HotSwap, реализованные в этой статье, находятся в https://github.com/lzggsimida123/hotswapsample, Вот краткое введение:
4.3.1 redefineClasses
redefineClasses позволяет нам повторно заменить класс JVM, теперь мы используем его для реализации простого требования, у нас есть следующий класс:
public class Test1 implements T1 {
public void sayHello(){
System.out.println("Test1");
}
}
Напечатайте Test1 в sayHello, затем мы вызовем sayHello в цикле основного метода:
public static void main(String[] args) throws Exception {
Test1 tt = new Test1();
int max = 20;
int index = 0;
while (++index<max){
Thread.sleep(100L);
}
}
Если мы ничего не делаем, Test1 должен быть напечатан 20 раз. Если мы хотим выполнить требование, эти 20 отпечатков поочередно распечатываются Test1, Test2, Test3. Затем мы можем использовать redefineClass.
//获取Test1,Test2,Test3的字节码
List<byte[]> bytess = getBytesList();
int index = 0;
for (Class<?> clazz : inst.getAllLoadedClasses()) {
if (clazz.getName().equals("Test1")) {
while (true) {
//根据index获取本次对应的字节码
ClassDefinition classDefinition = new ClassDefinition(clazz, getIndexBytes(index, bytess));
// redefindeClass Test1
inst.redefineClasses(classDefinition);
Thread.sleep(100L);
index++;
}
}
}
Вы можете видеть, что мы получили байт-коды трех классов, которые находятся в нашем корневом каталоге, а затем вызвали redefineClasses для замены наших соответствующих байт-кодов.Вы можете увидеть наши результаты и распечатать Test1, Test2 и Test3.
4.3.2 retransformClasses
redefineClasses напрямую меняет байт-код, что приводит к потере исходного байт-кода, который более ограничен. Используйте retransformClasses с нашим Transformer для преобразования байт-кода. Точно так же у нас есть следующий класс:
public class TestTransformer {
public void testTrans() {
System.out.println("testTrans1");
}
}
Для печати testTrans1 в testTrans у нас есть следующий основной метод:
public static void main(String[] args) throws Exception {
TestTransformer testTransformer = new TestTransformer();
int max = 20;
int index = 0;
while (++index<max){
testTransformer.testTrans();
Thread.sleep(100L);
}
Если ничего не делать, то надо напечатать testTrans1, а затем используем retransformClasses:
while (true) {
try {
for(Class<?> clazz : inst.getAllLoadedClasses()){
if (clazz.getName().equals("TestTransformer")) {
inst.retransformClasses(clazz);
}
}
Thread.sleep(100L);
}catch (Exception e){
e.printStackTrace();
}
}
Здесь мы просто пытаемся повторно преобразовать наш соответствующий класс, но требуется Transformer:
//必须设置true,才能进行多次retrans
inst.addTransformer(new SampleTransformer(), true);
Преобразователь добавлен выше. Если для него установлено значение false, он не будет выполняться при следующем повторном преобразовании класса, а сразу вернет код после его выполнения. Если установлено значение true, то пока есть вызов retransform, он будет выполняться.
public class SampleTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
if (!"TestTransformer".equals(className)){
//返回Null代表不进行处理
return null;
}
//进行随机输出testTrans + random.nextInt(3)
ClassReader reader = new ClassReader(classfileBuffer);
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
ClassVisitor classVisitor = new SampleClassVistor(Opcodes.ASM5,classWriter);
reader.accept(classVisitor,ClassReader.SKIP_DEBUG);
return classWriter.toByteArray();
}
}
}
Здесь SampleTransFormer использует ASM для замены кода и выполнения случайного вывода testTrans + random.nextInt(3). Вы можете увидеть следующие результаты:
Приведенный выше код был загружен на github:GitHub.com/host ГГ Смекта 1…
5. Некоторые распространенные проблемы
- В: Порядок транса в инструментарии Каков порядок исполнения нескольких трансформеров?
О: порядок выполнения следующий:
- Преобразователь, который реализует неретрансформируемые классы
- Native-Transformer, который реализует non-retransformClasses
- Выполните Transformer, который может retransformClasses
- Выполнить native-Transformer, который может retransformClasses
В пределах одного уровня обработка выполняется в порядке добавления.
- Вопрос: RefefineClass и RETRANSCLASS Разница?
A: Класс redefineClass не может быть возвращен к предыдущему, и наш Transformer не будет запущен, RetransClass затем выполнит Transformer, чтобы заменить класс в соответствии с текущим классом.
- В: Когда его заменят? Повлияет ли это на код, который я запускаю?
A: Объяснение в документе jdk состоит в том, что текущий вызов не будет затронут, и класс, который мы заменили, не будет загружен до конца этого вызова.
- Что можно изменить реконверсионным классом?
A: Повторное приведение может изменить тела методов, константные пулы и свойства. Повторное преобразование не может добавлять, удалять или переименовывать поля или методы, изменять сигнатуру метода или изменять наследование. В будущих версиях будет отменено (java8 не отменяется) 5. Какие классы байт-кода нельзя преобразовать?
A: Частные классы, такие как Integer.TYPE, и классы-массивы.
6. Что насчет JIT-кода?
A: Очистить исходный код JIT, а затем снова пройти процесс интерпретации и выполнения снова.
7. Как влияет на производительность arthas и jvm-sandbox?
A: Из-за добавления некоторой логики, это определенно повлияет, и при замене кода его необходимо заменить до тех пор, пока не будет выполнен SafePoint, и STW.Если код замены слишком частый, STW будет выполняться часто , что повлияет на производительность.
Суммировать
В этом году Arthas и jvm-sandbox с открытым исходным кодом от Ali способствовали разработке онлайн-инструментов диагностики Java. Если в будущем вы столкнетесь со сложными онлайн-проблемами, то Arthas должен быть одним из ваших предпочтительных целевых инструментов. Конечно, если вы хотите использовать некоторые общие компоненты, такие как сбор журналов, платформа Mock, имитация ошибок и т. д., jvm-sandbox может вам очень помочь. В то же время понимание их основных принципов также может очень помочь вам при настройке или устранении неполадок. Количество слов слишком много, я надеюсь, что каждый может получить полезные знания.
Справочная документация:
- Официальная документация по инструментарию jdk6-Api:docs.Oracle.com/java-color/6/do…
- arthas:GitHub.com/Alibaba/art…
- jvm-sandbox:GitHub.com/Alibaba/JVM…
Последняя статья была включена в JGrowing-CaseStudy, всеобъемлющий и отличный маршрут изучения Java, совместно созданный сообществом. Если вы хотите участвовать в обслуживании проектов с открытым исходным кодом, вы можете создать его вместе. Адрес github:GitHub.com/Java растет…Пожалуйста, дайте мне маленькую звезду.
Если вы считаете, что эта статья полезна для вас, то ваше внимание и пересылка - самая большая поддержка для меня, O(∩_∩)O: