как посмотреть байт код java
Просмотр байт-кода файла класса в Java
Изучите способы просмотра байт-кода файла класса в Java
1. Обзор
Анализ байт-кода является обычной практикой среди разработчиков Java по многим причинам, таким как поиск проблем с кодом, профилирование кода и поиск классов с конкретными аннотациями.
В этой статье мы рассмотрим способы просмотра байт-кода файла класса в Java.
2. Что такое байт-Код?
Байт-код-это промежуточное представление программы Java, позволяющее JVM переводить программу в инструкции по сборке на машинном уровне.
3. Использование java
Командная строка Java поставляется с инструментом javap , который отображает информацию о полях, конструкторах и методах файла класса.
Основываясь на используемых параметрах, он может разобрать класс и показать инструкции, которые содержат байт-код Java.
3.1. javap
Давайте используем команду javap для просмотра байт-кода наиболее распространенного класса Object :
Вывод команды покажет минимальную конструкцию класса Object :
Для просмотра всех классов и членов мы можем использовать аргумент -p :
Аналогично, мы можем использовать аргумент -v для просмотра подробной информации, такой как размер стека и аргументы для методов Объекта класса :
Кроме того, команда javap позволяет разбирать весь класс Java с помощью аргумента -c :
Кроме того, команда javap позволяет нам проверять информацию о системе, константы и сигнатуры внутренних типов, используя различные аргументы.
Теперь, когда мы увидели решение командной строки Java для просмотра байт-кода файла класса, давайте рассмотрим несколько библиотек для манипулирования байт-кодом.
4. Использование ASM
ASM-это популярная ориентированная на производительность низкоуровневая платформа для обработки и анализа байт-кода Java.
4.1. Настройка
Во-первых, давайте добавим последние зависимости asm и asm-util Maven в ваш pom.xml :
4.2. Просмотр байт-кода
Затем мы будем использовать ClassReader и TraceClassVisitor для просмотра байт-кода объекта класса:
Здесь мы отметим, что объект TraceClassVisitor требует, чтобы объект PrintWriter извлекал и создавал байт-код:
5. Использование BCEL
5.1. Зависимость Maven
Как обычно, давайте добавим последнюю зависимость bcel Maven в ваш pom.xml :
5.2. Разберите класс и просмотрите байт-код
Затем мы можем использовать класс Repository для создания объекта Java-класса :
Аналогично, методы set* доступны для манипулирования байт-кодом.
6. Использование Javassist
6.1. Зависимость Maven
Во-первых, мы добавим последнюю зависимость javassist Maven в ваш pom.xml :
6.2. Создание файла класса
Затем мы можем использовать Пул классов и Файл класса классы для создания класса Java :
Кроме того, объект файла Class class предоставляет доступ к пулу констант, полям и методам:
7. Jclasslib
7.1. Установка
Во-первых, мы установим плагин, используя диалоговое окно Настройки/Настройки:
7.2. Просмотр байт-кода класса объектов
Затем мы можем выбрать опцию “Показать байт-код с помощью Jclasslib” в меню Вид, чтобы просмотреть байт-код выбранного Объекта класса:
Затем откроется диалоговое окно, в котором будет показан байт-код класса Object :
7.3. Просмотр сведений
Кроме того, мы можем увидеть различные детали байт-кода, такие как постоянный пул, поля и методы, используя диалоговое окно плагина Jclasslib:
Аналогично, у нас есть плагин Bytecode Visualizer для просмотра байт-кода файла класса с помощью среды разработки Eclipse.
8. Заключение
В этом уроке мы рассмотрели способы просмотра байт-кода файла класса в Java.
Сначала мы рассмотрели команду javap вместе с ее различными аргументами. Затем мы рассмотрели несколько библиотек управления байт-кодом, которые предоставляют функции для просмотра и управления байт-кодом.
Введение в байт-код 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.
Введение в байт-код Java
May 15 · 6 min read
Каждому Java-разработчику известно, какую роль в экосистеме языка играет JVM. Однако большинство не разбирается в том, как работает JVM под капотом. Хотя для разработки на Java это не обязательно, код станет лучше, если вы глубже поймете JVM, потому что так вы будете знать, как каждая строка кода влияет на процессы внутри JVM.
Однако для начала нужно понять, что такое байт-код. Итак, поговорим о вводе и выводе байт-кода Java и о том, как он влияет на JVM во время запуска программы.
Что такое байт-код Java?
Если в какой-то момент профессиональной жизни вы слышали, как проповедуют независимость Java-программ от платформ, скажите спасибо байт-коду.
Как генерируется байт-код?
Как посмотреть байт-код Java?
Если вам хочется увидеть сам байт-код, простейший способ — воспользоваться командной строкой.
Как работает JVM
Прежде чем углубляться в байт-код, стоит понять, как JVM его обрабатывает.
Методы — одна из важнейших составляющих кода для JVM. Среда выполнения Java-программы — это, по сути, набор методов, вызываемых JVM. JVM создает фрейм для каждого такого метода и помещает созданный фрейм наверх стека текущего потока для выполнения.
Фрейм состоит из локальной среды, которая необходима для поддержания его выполнения. Как правило он содержит массив локальных переменных и стек операндов. Посмотрим, что эти элементы из себя представляют.
Массив локальных переменных
Массив локальных переменных, как следует из названия, нужен для хранения локальных переменных в методе. Также он хранит аргументы, которые принимает метод.
Определим два метода: один статический и один метод экземпляра, но схожие во всем остальном.
Локальные массивы переменных для этих методов будут выглядеть следующим образом:
Стек операндов
Стек операндов — это рабочее пространство внутри фрейма метода. Поскольку это стек, вы можете помещать и забирать значения только из верхней его части. Большинство инструкций байт-кода, принадлежащих определенному методу, либо участвуют в помещении значений в стек, либо забирают значения из стека для обработки.
Инструкция байт-кода load и ее расширения нужны для перемещения значения, хранящегося в массиве переменных, в стек. Инструкция store применяется для извлечения значений из стека и сохранения в массиве переменных. Существуют и другие инструкции, которые извлекают значения из стека для обработки.
Посмотрим в байт-код
Ради возможности вглядеться в байт-код, я написал простой Java-класс:
Деконструкция байт-кода
Здесь важно отметить еще одно: индексы, заданные инструкциям байт-кода — как видим, они не увеличиваются на единицу для каждой новой инструкции.
Число перед инструкцией указывает на индекс ее начального байта. А любой байт-код состоит из однобайтовых опкодов, за которыми следует ноль или более операндов.
Вывод
Надеюсь, вам удалось узнать кое-что новое о том, как работает байт-код Java. С этим более четким знанием вы сможете лучше писать код. Можете даже поэкспериментировать с самим байт-кодом во время выполнения программы, воспользовавшись такими библиотеками, как ASM.
Java байткод «Hello world»
На хабре уже есть статья про java байткод. Я решил ее немного дополнить и в меру сил развить тему. Мне кажется довольно логичным разобрать простейшее приложение на Java. А что может быть проще «Hello world»?
Для своего эксперимента я создал директорию src, куда в папку hello положил файл App.java:
Скопилируем файл командой:
На выходе в папке classes у меня появился файл App.class Для начала сравним размеры java и class файлов.
App.java 139B
App.class 418B
Это было неожиданно. Мне почему-то казалось, что скомпилированный файл должен быть меньше. Попытаюсь открыть class файл:
Довольно непривычный вид для Java кода. Попробуем с помощью описание формата class файлов понять, что здесь закодировано.
Это 4 байта для magic, который определяет формат файла.
minor version — Минорная версия как следует из названия
major version — 2 байта под мажорную версию.
Сочетание minor и major version говорит о том, что я компилировал этот код с помощью J2SE 8.
Эти два байта представляют constant_pool_count и отвечают за размер constant_pool. В моем случае count равен 29, а размер пула, соответственно 28. Дальше идут элементы вида:
cp_info <
u1 tag; // 1 байт на тег
u1 info[]; // массив с описанием
>
Рассмотрим элементы в constant_pool.
Этот тег соответствует CONSTANT_Methodref, а значит дальше должно быть описание:
CONSTANT_Methodref_info <
u1 tag;
u2 class_index;
u2 name_and_type_index;
>
соответственно:
class_index, указывает на 6 элемент в constant_pool
name_and_type_index, указывает на 15 элемент в constant_pool
Пока не понятно, на какой метод указывает эта ссылка и мы идем дальше:
Это CONSTANT_Fieldref, а значит дальше ищем:
CONSTANT_Fieldref_info <
u1 tag;
u2 class_index;
u2 name_and_type_index;
>
И тут все очень похоже на предыдущий элемент, хотя не понятно что это за поле, в своем классе я вроде ничего такого не объявлял.
class_index в 16 элементе
name_and_type_index в 17 элементе
tag для CONSTANT_String
получаем, что самое интересное лежит в 18 элементе:
Tag соответствующий ссылке на метод:
класс которого описан в 19 элементе
a название и тип в 20 элементе:
5-ый элемент:
Tag для CONSTANT_Class
название, которого в 21 элементе
6-ой элемент:
Cнова CONSTANT_Class
c названием в 22 элементе
Как мы помним 1-ый элемент constant_pool относится к этому классу.
7-ой элемент:
tag, CONSTANT_Utf8, первая строчка
Она должна соответствовать:
CONSTANT_Utf8_info <
u1 tag;
u2 length;
u1 bytes[length];
>
Тогда длина нашей строчки 6 байт:
Это особое название, так помечаются конструкторы.
строчка длины 3 — «()V»:
Это описание нашего конструктора без параметров, который был упомянут в седьмом элементе.
9-ый элемент:
CONSTANT_Utf8
10-ый элемент:
Строка LineNumberTable
15-ый элемент
Tag, соответствует CONSTANT_NameAndType
а значит нам понадобится
CONSTANT_NameAndType_info <
u1 tag;
u2 name_index;
u2 descriptor_index;
>
и тогда:
ссылка на 7 элемент
ccылка на 8 элемент
Учитывая что первый элемент ссылался на это, мы можем заключить что первым был объявлен конструктор класса без параметров. Название класса, мы должны найти в 22 элементе.
16-ый элемент:
Tag, для CONSTANT_Class
c названием в 23 элементе
17-ый элемент:
Tag, CONSTANT_NameAndType, со ссылкой на 24 и 25 элемент constant_pool
18-ый элемент:
Ура «Hello world!»
19-ый элемент:
Tag, для CONSTANT_class c названием в 25-ом элементе
20-ый элемент:
Tag CONSTANT_NameAndType cо ссылкой на 27 и 28 элемент
25-ый элемент:
«Ljava/io/PrintStream;»
26-ой элемент:
«java/io/PrintStream»
methods_count у нас 2 метода в классе, конструктор по умолчанию и метод main:
Method 1 — Constructor
Один из самых интересных аттрибутов с кодом нашего метода code[code_length], разбор инструкций отдельная большая тема:
Аттрибут закончился и продолжается описание метода
Attribute 1 код метода main
Описание методов закончено и идет описание атрибутов класса
Теперь когда мы закончили с по-байтовым разбором class файла, становится понятно как работает:
Он автоматически выводит тоже самое, что я выписал руками:
А вот здесь можно посмотреть пример разбора class файла:
Введение в байт-код 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.