tgoop.com/cxx95/125
Last Update:
#compiler
На фото типичная задняя обложка журнала или тетради в нулевые годы. Сейчас такого не увидишь (закон не позволяет или устарело?), но в то время я выбирал тетради с самым забитым задником, чтобы было на что посмотреть в школе
На кнопочном телефоне у меня было не так много Java-игр, плюс товарищи могли что-то передать по ИК-порту. Но однажды мне купили ШНУР по которому можно было с компа перекинуть все, и я стал "устанавливать все игры"
Ломая глаза об экран 240x320, я прошел все популярные игры, а для Gravity Defied я прошел все моды и даже пытался менять в .jar
-файле что-то сам, получилось поставить картинку на фон и поменять окраску мотыка
Обзор байткода Java
Язык
Эти направления давно умерли, но язык живет - он популярен для разработки в Android, в финтехе и бэкенде (лучший в мире фреймворк для бэкенда
Есть несколько реализаций компилятора Java и виртуальных машин.
Язык довольно простой, потому что на него наложена куча ограничений:
Весь код должен находиться внутри классов.
Тело функции надо писать сразу (нельзя как в C++ только "объявить" функцию).
Принцип "1 класс = 1файл" - в файле Foo.java
должно находиться определение ровно одного класса с именем Foo
.
Все типы, кроме примитивных (int, double, и пр.), неявно наследуются от класса java.lang.Object
. Все методы виртуальные (кроме статических методов).
Генерики в языке - просто развод, их по факту нет. Они добавлены аж в 2004 году как синтаксический сахар
Все ссылки на объекты указывают на java.lang.Object
, а в коде делается неявное приведение к типу.
То есть запись ArrayList<Foo>
это самообман - в действительности хранятся ссылки не на Foo
, а на java.lang.Object
, просто есть неявное приведение к Foo
и компилятор чекнет что в твоем коде нигде нет некорректных приведений
Чтобы примитивные типы можно было использовать в контейнерах, для них сделаны типы-"обертки", наследующиеся от java.lang.Object
. Для int
это Integer
, для double
это Double
, и так далее.
Нельзя сделать ArrayList<int>
, можно сделать ArrayList<Integer>
.
Можно оценить оверхед - если std::vector<int>
из 128 элементов это 4*128 последовательных байт в памяти, то ArrayList<Integer>
из тех же элементов это 128 объектов (которые находятся хрен знает где в куче), каждый является оберткой над int
-ом
Хотя в Java это нормально - он не задумывался как супер вычислительный язык.
После компиляции каждый Foo.java
преобразуется в Foo.class
, потом они все попадают в "исполняемый" .jar
-файл, который является просто zip-архивом 📦
В начале .class
-файла находится "constant pool", это константы (строки, длинные числа, и прочее).
Потом в файле находятся функции класса, скомпилированные в байткод.
В супер понятной официальной спеке можно увидеть примерный вывод компилятора в разных случаях, формат .class
-файла, что должна делать виртуальная машина, и прочее.
Байткод очень простой - каждая команда состоит из 1 байта, называется "опкод", после него может быть 1-2 байта дополнительной инфы, если надо.
Команды разбиты на ~20 групп из почти одинаковых опкодов, список тут. Многие высокоуровневые, например опкод arraylength
который положит на стек размер массива.
Компилятор (который переводит язык в .class
-файлы) не делает никаких предположений о memory layout объекта, поэтому вызов метода выглядит так:
invokevirtual #4 // каждый метод (кроме статических) является виртуальным
Где
#4
это 4-й объект в "constant pool", являет собой "символическую ссылку", то есть тупо строку Если это метод
int addtwo(int a, int b)
в классе Example
, то ссылка такая: Example.addtwo(II)I
Виртуальная машина должна сама все отрезольвить и подставить адрес метода.
ПРОДОЛЖЕНИЕ В КОММЕНТАРИИ