как будет выглядеть команда для запуска класса test готовый байт код которого
Структура байт-кода виртуальной машины Java
В последнее время на Хабре появились статьи которые затрагивают манипуляцию байт-кода. Что заставило меня опубликовать следую статью посвященную его структуре.
У платформы java имеется две особенности. Для обеспечения кроссплатформенности программа сначала компилируется в промежуточный язык низкого уровня — байт-код. Вторая особенность загрузка исполняемых классов происходит с помощью расширяемых classloader. Это механизм обеспечивает большую гибкость и позволяет модифицировать исполняемый код при загрузке, создавать и подгружать новые классы во время выполнения программы.
Такая техника широко применяется для реализации AOP, создания тестовых фреймворков, ORM. Особенно хочется отметить terracotta, продукт с красивой идеей кластеризации jvm и на всю катушку использующей модификации байт-кода. Эта заметка будет посвящена обзору структуры байт-кода, первой части этой сильной связки.
Каждому классу в java соответствует один откомпилированный файл. Это справедливо даже для подклассов или анонимным классов. Такой файл содержит информацию об имени класса, его родителе, список интерфейсов которые он реализует, перечисление его полей и методов. Важно отметить, что после компиляции информации, которая содержит директива import, теряется и все классы именуются теперь через полный путь. Например в место String будет записано java/lang/String.
Самое интересное как будут выглядеть методы класса в байт-коде. Будем наблюдать во что трансформируется следующий класс:
Начнем с заголовка. В нем содержится информация о название метода, то, что метод вызывается без параметров, и тип возвращаемого аргумента.
Байт-код стеко-ориентированный язык, похожий по своей структуре на ассемблер. Что бы произвести операции с данными их сначала нужно положить на стек. Мы хотим взять поле у объекта. Что бы это сделять нужно его положить в стек. В байт-коде нет имен переменных, у них есть номера. Нулевой номер у ссылки на текущий объект или у переменой this. Потом идут параметры исполняемого метода. Затем остальные переменные.
Команда ALOAD 0 кладет переменную this на стек. Что бы на стек положить тип данных, отличный от ссылки, нужно воспользоваться другой командой. Для long будет LLOAD, а для doubles[] будет DALOAD.
Следующая команда GETFIELD, убирает со стека ссылку на объект и кладет примитивный тип или ссылку на поле данного объекта. У нее есть два параметра. Первый имя — класса, второй имя — переменной. Если же переменная статическая, то предварительно класть на стек ничего не нужно, а команду нужно заменить на GETSTATIC с теми же параметрами.
Последняя команда говорит, что метод завершен и возвращает значения типа ссылки со стека.
Сеттер имеет немного более сложную структуру.
Данный метод ничего не возвращает. Первые две команды кладут на стек переменную this и параметр исполняемого метода. Затем вызывается команда PUTFIELD (PUTSTATIC для статического поля) которая установит значение поля объекта и уберет со стека последние два значения. Последняя команда — выход из метода.
Добавим к нашему объект еще пару методов и посмотрим, какой байт-код им соответствует.
testMethod имеет следующее представление.
Первая команда вызывает статический метод у класса System. Вторая запоминает результат вызова метода currentTimeMillis в переменной со вторым номером. Затем мы кладем переменную this, параметр метода и переменную с номером 2 на стек. Преобразую переменную к типу java/lang/Long. И проверяем, что она у нас содержится в коллекции, вызывая метод у параметра исполняемого. У нас параметр интерфейс, поэтому применяется команда INVOKEINTERFACE. Для метода класса необходимо использовать INVOKEVIRTUAL. Чтобы вызвать метод у объект или интерфейса необходимо, чтобы на стеке лежал объект, затем параметры вызываемого метода. В результате вызова метода они заменятся на результат или просто уберутся со стека, если метод ничего возвращает. Последняя три команды кладут перемену на стек, превращают ее в объект и возвращают ее как значение метода.
Чтобы завершить наш экскурс в байт-код, добавим последний метод и посмотрим на циклы и условные операторы.
Он в байт-коде будет выглядить так
Дальше у нас начинается тело цикла. Для реализации которого потребуются метки,
команда перехода и условный оператор. В теле нашего метода аж целых три меток. Первая метка означает начало цикла. Вторая нужна для условного оператора, а последняя знаменует конец цикла. Уловный оператор имеет один параметр метку перехода. Прежде чем его вызвать сравниваемы значения должны лежать на стеке. Для сравнения с нулем используется отдельная команда. Для каждого типа есть свой оператор сравнения. Для int он IF_ICMPGE. После сравнения сравниваемые значения убираются со стека. Для арифметических действий с двумя переменным их так же как и для условного оператора нужно предварительно положить на стек. После выполнения они снимаются со стека, а на их место кладется результат.
На это краткий экскурс в байт-код закончен, некоторые вопросы такие как исключения, синхронизация не были затронуты. Я надеюсь, что имея представление о байт коде читатель без труда справиться с ними. В следующей части мы рассмотрим инструменты которые применяются для модификации байт-кода.
Как будет выглядеть команда для запуска класса test готовый байт код которого
. это один из основных кадровых документов директивного характера, отражающий номенклатуру рабочих мест в структурных подразделениях организации и установленные по ним должностные оклады
Кадровая стратегия
Должностная инструкция
Штатное расписание
. это часть населения страны, способная к трудовой деятельности
Человеческий фактор
Человеческий капитал
Трудовые ресурсы
Планирование процесса функционирования и перспективного развития системы управления персоналом (включая конкретные плановые документы текущего характера и целевые программы) относится к такому направлению кадровой работы, как.
формирование кадровой стратегии
кадровое планирование
регулирование численности персонала
кадровый учет и отчетность
Основной целью политики развития персонала является …
полное удовлетворение потребностей организации в трудовых ресурсах
создание у сотрудников постоянной заинтересованности в обеспечении запланированных работодателем трудовых результатов
обеспечение соответствия квалификационного уровня сотрудников требованиям работодателя
Для противодействия конфликту, уже вышедшему на фазу кризисного развития, более целесообразно использовать методы …
административного характера
социального характера
психологического характера
… детально регламентирует функциональные обязанности и права нанимаемого сотрудника
Трудовой договор
Должностная инструкция
Типовой перечень требований по рабочему месту
Информация о конкретном содержании индивидуальных социальных пакетов сотрудников организации.
носит строго конфиденциальный характер
носит открытый характер
имеет статус информации «для служебного пользования»
Смешанный стиль управления предполагает ориентацию руководителя на реализацию различных стилей управления в зависимости от конкретной ситуации – так, в кризисной ситуации руководитель может использовать … стиль руководства
авторитарный
демократический
либеральный
демократический или либеральный
За результаты психологической адаптации вновь нанятого сотрудника в первую очередь отвечает …
руководитель структурного подразделения
психолог службы персонала
руководитель службы персонала
В форме угроз финансовых и рыночных потерь из-за отсутствия необходимых сотрудников (как более опасная угроза) или временного переизбытка персонала проявляются …
кадровые риски количественного характера
кадровые риски качественного характера
риски лояльности персонала
Для противодействия конфликту, уже вошедшему в фазу кризисного развития, наиболее целесообразно использовать методы … характера
социального
психологического
административного
К критериям для оценки количественных параметров человеческого капитала можно отнести динамику … показателя, отражающего процесс повышения квалификации персонала, с выделением всех форм дополнительного обучения
удельного веса сотрудников, включенных в резерв на выдвижение
показателя «текучести кадров», с выделением показателя, отражающего число сотрудников, уволившихся по собственному желанию
пропорции между профессиональными категориями сотрудников («менеджеры», «эксперты», «исполнители»)
Кадровые риски качественного характера проявляются в имущественных и неимущественных потерях организации вследствие …
недостатка или переизбытка персонала
недостаточной ответственности или квалификации персонала
недостаточной лояльности персонала своему работодателю
Вертикальный тип профессиональной карьеры предполагает развитие сотрудника в организации путем …
периодического повышения его в должности
перевода его с одного участка работы на другой с последовательным повышением и его должностного статус
перевода его с одного участка работы на другой с последовательным повышением и его должностного статуса
. стиль управления это стиль руководства, предполагающий опору на мнение большинства в целях обеспечения личной популярности в коллективе и негласное обеспечение приоритета интересов своего подразделения над интересами организации в целом
Кадровые риски качественного характера проявляются в форме …
финансовых и рыночных потерь из-за злоупотреблений доверием работодателя со стороны его собственных нелояльных сотрудников
финансовых и рыночных потерь из-за недостаточной квалификации или ответственности
сотрудников
угроз финансовых и рыночных потерь из-за отсутствия необходимых сотрудников (как более опасная угроза) или временного переизбытка персонала
. форма оплаты труда предусматривает выплату специальная премия в заранее оговоренных размерах при перевыполнении нормы выработки
Сдельно-премиальная
Сдельно-регрессивная
Сдельно-прогрессивная
это высший орган исполнительного управления организацией, включающий в себя директора и всех его заместителей
Дирекция
Совокупность стратегических целей организации по кадровому направлению деятельности и принципиальных подходов к их практической реализации – это …
кадровая стратегия
кадровое перемещение
кадровая политика
На рынке труда рекрутинговое агентство … посредника
может выступать в роли как государственного, так и негосударственного
выступает в роли государственного
выступает в роли негосударственного
При коррекции психологического климата методы психологического характера обычно применяются …
на ранних стадиях развития выявленного конфликта или при относительно незначительном
ухудшении психологического климата
как на ранней стадии развития конфликта, так и после выхода его в стадию кризисного развития.
только после выхода конфликта в стадию кризисного развития
1. Что из перечисленного может быть отнесено к преимуществам шаблона «одиночка»?
— ускорение начальной инициализации;
— минимизация задержки при обращении в силу статической сущности;
— сокращение числа имен в глобальном адресном пространстве;
— предоставление глобальной точки доступа;
2. Что из перечисленного может быть отнесено к преимуществам шаблона «строитель»?
— позволяет изменять внутреннее представление продукта;
— изолирует код, реализующий конструирование и представление;
— дает более тонкий контроль над процессом конструирования;
3. Что из перечисленного может быть отнесено к преимуществам шаблона «функциональный дизайн»?
— максимально низкая связь между модулями;
— каждый модуль имеет только одну обязанность и исполняет ее с минимальным влиянием на другие части программы;
— упрощает код модулей;
— позволяет безопасно повторно использовать код;
— простота кода модулей позволяет добиться простоты архитектуры и упрощение процедуры внесения изменений в дальнейшем;
— повседневные задачи: переадресация работы, обеспечение слабой связности системы и так далее;
4. Что из перечисленного может быть отнесено к преимуществам шаблона «фабричный метод»?
— положительно сказывается на скорости работы;
— сокращает количество имен в адресном пространстве;
— позволяет сделать код создания объектов более универсальным;
— позволяет устанавливать связь между параллельными иерархиями классов;
5. Какие из перечисленных утверждений касательно высокоуровневых шаблонов проектирования верны?
— высокоуровневые шаблоны также называют архитектурными
— высокоуровневые шаблоны также называют идиомами;
— несмотря на то, что высокоуровневые шаблоны являются универсальными, они учитывают специфические особенности конкретных языков программирования;
— высокоуровневые шаблоны являются универсальными решениями;
7.Что из перечисленного может быть отнесено к преимуществам шаблона «команда»?
— позволяет параметризировать объекты выполняемым действием, ставить запросы в очередь, отменять запросы, протоколировать проделанные изменения и так далее
— может быть создана структурированная на основе высокоуровневых операций легко изменяемая система
— определяют алгоритмы и способы взаимодействия объектов между собой;
8.Что из перечисленного может быть отнесено к преимуществам шаблона «шаблонный метод»?
— позволяет оптимизировать передачу данных при вызове методов;
— не требует дополнительной иерархии классов;
— помогает избегать дублирования повторяющихся конструкций;
— позволяет динамически варьировать поведение системы;
9.Что из перечисленного может быть отнесено к преимуществам шаблона «цепочка ответственности»?
— конкретный обработчик запроса клиента заранее неизвестен и должен быть найден автоматически;
— набор объектов, способных обрабатывать запросы, должен динамически меняться;
— ослабление связности (клиентскому объекту нет нужды знать что-либо об объектах, обслуживающих его запрос; достаточно только иметь ссылку на точку входа);
— дополнительная гибкость при распределении обязанностей (цепочка классов-обработчиков может быть модифицирована с минимальными затратами);
— определяют алгоритмы и способы взаимодействия объектов между собой;
— поиск наилучшей стратегии, в связанном списке объектов стратегий;
10.Что из перечисленного может быть отнесено к преимуществам шаблона «заместитель»?
— позволяет выполнять инициализацию объектов по требованию;
— обладает каким-либо непосредственно связанным с замещаемым объектом дополнительным функционалом (в данном случае – возможностью сообщить реальные размеры изображения без ее загрузки);
— появление нового уровня функциональности п
Java Bytecode Fundamentals
Разработчики приложений на Java обычно не нуждаются в знании о байт-коде, выполняющемся в виртуальной машине, однако тем, кто занимается разработкой современных фреймворков, компиляторов или даже инструментов Java может понадобиться понимание байт-кода и, возможно, даже понимание того, как его использовать в своих целях. Несмотря на то, что специальные библиотеки типа ASM, cglib, Javassist помогают в использовании байт-кода, необходимо понимание основ для того, чтобы использовать эти библиотеки эффективно.
В статье описаны самые основы, от которых можно отталкиваться в дальнейшем раскапывании данной темы (прим. пер.).
Давайте начнём с простого примера, а именно POJO с одним полем и геттером и сеттером для него.
Когда вы скомпилируете класс, используя команду javac Foo.java, у вас появится файл Foo.class, содержащий байт-код. Вот как его содержание выглядит в HEX-редакторе:
Каждая пара шестнадцатеричных чисел (байт) переводится в опкоды (мнемоника). Было бы жестоко попытаться прочитать это в двоичном формате. Давайте перейдем к мнемоничному представлению.
Класс очень простой, поэтому будет легко увидеть связь между исходным кодом и сгенерированным байт-кодом. Первым делом мы видим, что в байт-код-версии класса компилятор вызывает конструктор по умолчанию (как и написано в спецификациях JVM).
Далее, изучая байт-кодовые инструкции (у нас это aload_0 и aload_1), мы видим, что некоторые из них имеют префиксы типа aload_0 и istore_2. Это относится к типу данных, с которыми оперирует инструкция. Префикс «a» обозначает, что опкод управляет ссылкой на объект. «i», соответственно, управляет integer.
Теперь видно, что это за странные операнды. Например, #2:
const #2 = Field #3.#18; // Foo.bar:Ljava/lang/String;
const #3 = class #19; // Foo
const #18 = NameAndType #5:#6;// bar:Ljava/lang/String;
Отметим, что, каждый код операции помечен номером (0: aload_0). Это указание на позицию инструкции внутри фрейма — дальше объясню, что это значит.
Чтобы понять, как работает байт-код, достаточно взглянуть на модель выполнения. JVM использует модель выполнения на основе стеков. Каждый тред имеет JVM-стек, содержащий фреймы. Например, если мы запустим приложение в дебаггере, то увидим следующие фреймы:
При каждом вызове метода создается новый фрейм. Фрейм состоит из стека операнда, массива локальных переменных и ссылку на пул констант класса выполняемого метода.
Размер массива локальных переменных определяется во время компиляции в зависимости от количества и размера локальных переменных и параметров метода. Стек операндов — LIFO-стек для записи и удаления значений в стеке; размер также определяется во время компиляции. Некоторые опкоды добавляют значения в стек, другие берут из стека операнды, изменяют их состояние и возвращают в стек. Стек операндов также используется для получения значений, возвращаемых методом (return values).
Байткод для этого метода состоит из трёх опкодов. Первый опкод, aload_0, проталкивает в стек значение с индексом 0 из таблицы локальных переменных. Ссылка this в таблице локальных переменных для конструкторов и instance-методов всегда имеет индекс 0. Следующий опкод, getfield, достает поле объекта. Последняя инструкция, areturn, возвращает ссылку из метода.
Так, байткод для метода getBar — 2A B4 00 02 B0. 2A относится к инструкции aload_0, B0 — к areturn. Может показаться странным, что байткод для метода имеет три инструкции, а в массиве байт 5 элементов. Это связано с тем, что getfield (B4) нуждается в двух параметрах (00 02), занимающих позиции 2 и 3 в массиве, отсюда и 5 элементов в массиве. Инструкция areturn сдвигается на 4 позицию.
Таблица локальных переменных
Для иллюстрации того, что происходит с локальными переменными, воспользуемся ещё одним примером:
Здесь две локальных переменных — параметр метода и локальная переменная int b. Вот как выглядит байт-код:
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this LExample;
0 6 1 a I
2 4 2 b I
Метод загружает константу 1 с помощью iconst_1 и ложит её в локальную переменную 2 с помощью istore_2. Теперь в таблице локальных переменных слот 2 занят переменной b, как и ожидалось. Далее, iload_1 загружает значение в стек, iload_2 загружает значение b. iadd выталкивает 2 операнда из стека, добавляет их и возвращает значение метода.
Обработка исключений
Интересный пример того, какой получается байт-код в случае с обработкой исключений, например, для конструкции try-catch-finally.
Байт-код для метода foo():
Компилятор генерирует код для всех сценариев, возможных внутри блока try-catch-finally: finallyMethod() вызывается три раза(!). Блок try скомпилировался так, как будто try не было и он был объединён с finally:
0: aload_0
1: invokespecial #2; //Method tryMethod:()V
4: aload_0
5: invokespecial #3; //Method finallyMethod:()V
Если блок выполняется, то инструкция goto перекидывает выполнение на 30-ю позицию с опкодом return.
Если tryMethod бросит Exception, будет выбран первый подходящий (внутренний) обработчик исключений из таблицы исключений. Из таблицы исключений мы видим, что позиция с перехватом исключения равна 11:
0 4 11 Class java/lang/Exception
Это перекидывает выполнение на catchMethod() и finallyMethod():
11: astore_1
12: aload_0
13: invokespecial #5; //метод catchMethod:()V
16: aload_0
17: invokespecial #3; //метод finallyMethod:()V
Если в процессе выполнения будет брошено другое исключение, мы увидим, что в таблице исключений позиция будет равна 23:
0 4 23 any
11 16 23 any
23 24 23 any
Инструкции, начиная с 23:
23: astore_2
24: aload_0
25: invokespecial #3; //Method finallyMethod:()V
28: aload_2
29: athrow
30: return
Так что finallyMethod() будет выполнен в любом случае, с aload_2 и athrow, бросающим необрабатываемое исключение.
Это всего лишь несколько моментов из области байткода JVM. Большинство было почерпнуто из статьи developerWorks Peter Haggar — Java bytecode: Understanding bytecode makes you a better programmer. Статья немного устарела, но до сих пор актуальна. Руководство пользователя BCEL содержит достойное описание основ байт-кода, поэтому я предложил бы почитать его интересующимся. Кроме того, спецификация виртуальной машины также может быть полезным источником информации, но ее нелегко читать, кроме этого отсутствует графический материал, который бывает полезным при понимании.
В целом, я думаю, что понимание того, как работает байт-код, является важным моментом в углублении своих знаний в Java-программировании, особенно для тех, кто присматривается к фреймворкам, компиляторам JVM-языков или другим утилитам.
Введение в байт-код Java
Каждому Java-разработчику известно, какую роль в экосистеме языка играет JVM. Однако большинство не разбирается в том, как работает JVM под капотом. Хотя для разработки на Java это не обязательно, код станет лучше, если вы глубже поймете JVM, потому что так вы будете знать, как каждая строка кода влияет на процессы внутри JVM.
Однако для начала нужно понять, что такое байт-код. Итак, поговорим о вводе и выводе байт-кода Java и о том, как он влияет на JVM во время запуска программы.
Что такое байт-код Java?
Если в какой-то момент профессиональной жизни вы слышали, как проповедуют независимость Java-программ от платформ, скажите спасибо байт-коду.
Байт-код — это набор команд, который JVM применяет для запуска программы. Поскольку байт-код, сгенерированный для программы, не зависит от платформы, где она запущена, вы можете без проблем запускать свою программу на любой машине, на которой есть JVM для интерпретации байт-кода.
Как генерируется байт-код?
Как посмотреть байт-код Java?
Если вам хочется увидеть сам байт-код, простейший способ — воспользоваться командной строкой.
Как работает JVM
Прежде чем углубляться в байт-код, стоит понять, как JVM его обрабатывает.
Методы — одна из важнейших составляющих кода для JVM. Среда выполнения Java-программы — это, по сути, набор методов, вызываемых JVM. JVM создает фрейм для каждого такого метода и помещает созданный фрейм наверх стека текущего потока для выполнения.
Фрейм состоит из локальной среды, которая необходима для поддержания его выполнения. Как правило он содержит массив локальных переменных и стек операндов. Посмотрим, что эти элементы из себя представляют.
Массив локальных переменных
Массив локальных переменных, как следует из названия, нужен для хранения локальных переменных в методе. Также он хранит аргументы, которые принимает метод.
Определим два метода: один статический и один метод экземпляра, но схожие во всем остальном.
Локальные массивы переменных для этих методов будут выглядеть следующим образом:
Стек операндов
Стек операндов — это рабочее пространство внутри фрейма метода. Поскольку это стек, вы можете помещать и забирать значения только из верхней его части. Большинство инструкций байт-кода, принадлежащих определенному методу, либо участвуют в помещении значений в стек, либо забирают значения из стека для обработки.
Инструкция байт-кода load и ее расширения нужны для перемещения значения, хранящегося в массиве переменных, в стек. Инструкция store применяется для извлечения значений из стека и сохранения в массиве переменных. Существуют и другие инструкции, которые извлекают значения из стека для обработки.
Посмотрим в байт-код
Ради возможности вглядеться в байт-код, я написал простой Java-класс:
Деконструкция байт-кода
Здесь важно отметить еще одно: индексы, заданные инструкциям байт-кода — как видим, они не увеличиваются на единицу для каждой новой инструкции.
Число перед инструкцией указывает на индекс ее начального байта. А любой байт-код состоит из однобайтовых опкодов, за которыми следует ноль или более операндов.
Вывод
Надеюсь, вам удалось узнать кое-что новое о том, как работает байт-код Java. С этим более четким знанием вы сможете лучше писать код. Можете даже поэкспериментировать с самим байт-кодом во время выполнения программы, воспользовавшись такими библиотеками, как ASM.