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Виртуальная машина должна сама все отрезольвить и подставить адрес метода.
ПРОДОЛЖЕНИЕ В КОММЕНТАРИИ
