#compiler
На фото типичная задняя обложка журнала или тетради в нулевые годы. Сейчас такого не увидишь (закон не позволяет или устарело?), но в то время я выбирал тетради с самым забитым задником, чтобы было на что посмотреть в школе
🔍 На кнопочном телефоне у меня было не так много
Java-игр, плюс товарищи могли что-то передать по ИК-порту. Но однажды мне купили
ШНУР по которому можно было с компа перекинуть все, и я стал "устанавливать все игры"
🖥 Ломая глаза об экран 240x320, я прошел все популярные игры, а для Gravity Defied я прошел все моды и даже пытался менять в
.jar
-файле что-то сам, получилось поставить картинку на фон и поменять окраску мотыка
⌨️ Обзор байткода Java 👩💻 Язык
👩💻Java появился в 1995 году и прошел большую эволюцию. Сначала он использовался для апплетов, выполняющихся в браузере. Потом для приложений Nokia и прочих кнопочников (подмножество J2ME).
Эти направления давно умерли, но язык живет - он популярен для разработки в Android, в финтехе и бэкенде (лучший в мире фреймворк для бэкенда
👩💻 Spring), есть много проектов (Kafka, ZooKeeper, etc.), появились интероперабельные языки -
👩💻Kotlin,
👩💻Scala, Closure.
Есть несколько реализаций компилятора 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
Виртуальная машина должна сама все отрезольвить и подставить адрес метода.
ПРОДОЛЖЕНИЕ В КОММЕНТАРИИ