Введение
Исполняемый формат файла Java – формат данных.
the data format
Файл класса Java – это файл (с расширением имени файла), содержащий байт-код Java, который может быть выполнен на виртуальной машине Java (JVM). Файл класса Java обычно создается компилятором Java из исходных файлов (файлов) языка программирования Java, содержащих классы Java (в качестве альтернативы, другие языки JVM также могут использоваться для создания файлов классов). Если исходный файл содержит более одного класса, каждый класс компилируется в отдельный файл класса. Виртуальные машины Java доступны для многих платформ, и файл класса, скомпилированный на одной платформе, будет выполняться на JVM другой платформы. Это обеспечивает платформенную независимость Java-приложений.
История
11 декабря 2006 года формат файла класса был изменен в рамках Java Specification Request (JSR) 202.
Магическое число
Файлы классов идентифицируются следующим 4-байтным заголовком (в шестнадцатеричном формате): CA FE BA BE (первые 4 записи в таблице ниже). Джеймс Гослинг объяснил историю этого "магического числа", ссылаясь на ресторан в Пало-Альто: "Мы часто обедали в заведении под названием St Michael's Alley. По местной легенде, в давние времена там выступали Grateful Dead, до того как они стали знаменитыми. Это было довольно необычное место, которое явно подходило для поклонников Grateful Dead. Когда умер Джерри, они даже установили небольшой буддийский алтарь. Когда мы там бывали, мы называли это место "Кафе Мертвецов". В какой-то момент кто-то заметил, что это шестнадцатеричное число. Я перерабатывал код формата файла и мне понадобилось несколько "магических чисел": одно для файла постоянных объектов, а другое для классов. Я использовал CAFEDEAD для формата файла объектов, а при поиске 4-символьных шестнадцатеричных слов, подходящих после "CAFE" (казалось, это хорошая тема), наткнулся на BABE и решил его использовать. В то время это не казалось особенно важным или предназначенным для чего-то иного, кроме как для мусорной корзины истории. Так CAFEBABE стал форматом файла класса, а CAFEDEAD – форматом файла постоянных объектов. Но функциональность постоянных объектов исчезла, и вместе с ней исчезло использование CAFEDEAD, которое в конечном итоге было заменено RMI."
"We used to go to lunch at a place called St Michael's Alley. According to local legend, in the deep dark past, the Grateful Dead used to perform there before they made it big. It was a pretty funky place that was definitely a Grateful Dead Kinda Place. When Jerry died, they even put up a little Buddhist esque shrine. When we used to go there, we referred to the place as Cafe Dead. Somewhere along the line it was noticed that this was a HEX number. I was re vamping some file format code and needed a couple of magic numbers: one for the persistent object file, and one for classes. I used CAFEDEAD for the object file format, and in grepping for 4 character hex words that fit after "CAFE" (it seemed to be a good theme) I hit on BABE and decided to use it. At that time, it didn't seem terribly important or destined to go anywhere but the trash can of history. So CAFEBABE became the class file format, and CAFEDEAD was the persistent object format. But the persistent object facility went away, and along with it went the use of CAFEDEAD it was eventually replaced by RMI."
Постоянный пул
Таблица констант – это место хранения большинства литеральных постоянных значений. Сюда входят такие значения, как числа всех типов, строки, имена идентификаторов, ссылки на классы и методы, а также дескрипторы типов. Все индексы, или ссылки, на конкретные константы в таблице констант задаются 16-битными числами (типа u2), где значение индекса 1 соответствует первой константе в таблице (значение индекса 0 недействительно). В силу исторических решений, принятых при разработке формата файла, количество констант в таблице констант не совпадает с количеством констант, предшествующим таблице. Во-первых, таблица индексируется, начиная с 1 (а не с 0), но счетчик следует интерпретировать как максимальный индекс плюс один. Кроме того, два типа констант (long и double) занимают два последовательных слота в таблице, хотя второй слот является фиктивным индексом, который никогда не используется напрямую. Тип каждого элемента (константы) в таблице констант идентифицируется начальным байт-тегом. Количество байтов, следующих за этим тегом, и их интерпретация зависят от значения тега. Допустимые типы констант и их теги:
Tag byte Дополнительные байты Описание константы Версия введена 1 2+x байт (переменная) UTF-8 (Unicode) строка: строка символов, которой предшествует 16-битное число (тип u2), указывающее количество байтов в закодированной строке, которая следует сразу за ним (которое может отличаться от количества символов). Обратите внимание, что используемая кодировка фактически не UTF-8, а включает небольшое изменение стандартной формы кодировки Unicode. 1.0.2 3 4 байта Целое число: 32-битное знаковое число в дополнительном коде в формате big endian 1.0.2 4 4 байта Число с плавающей точкой: 32-битное число с плавающей точкой одинарной точности IEEE 754 1.0.2 5 8 байт Long: 64-битное знаковое число в дополнительном коде в формате big endian (занимает два слота в таблице констант) 1.0.2 6 8 байт Double: 64-битное число с плавающей точкой двойной точности IEEE 754 (занимает два слота в таблице констант) 1.0.2 7 2 байта Ссылка на класс: индекс в таблице констант к строке UTF-8, содержащей полное имя класса (во внутреннем формате) (big endian) 1.0.2 8 2 байта Ссылка на строку: индекс в таблице констант к строке UTF-8 (также big endian) 1.0.2 9 4 байта Ссылка на поле: два индекса в таблице констант, первый указывает на ссылку на класс, второй – на дескриптор имени и типа (big endian) 1.0.2 10 4 байта Ссылка на метод: два индекса в таблице констант, первый указывает на ссылку на класс, второй – на дескриптор имени и типа (big endian) 1.0.2 11 4 байта Ссылка на метод интерфейса: два индекса в таблице констант, первый указывает на ссылку на класс, второй – на дескриптор имени и типа (big endian) 1.0.2 12 4 байта Дескриптор имени и типа: два индекса к строкам UTF-8 в таблице констант, первый представляет имя (идентификатор), а второй – специально закодированный дескриптор типа. 1.0.2 15 3 байта Ручка метода: эта структура используется для представления ручки метода и состоит из одного байта дескриптора типа, за которым следует индекс в таблице констант. 7 16 2 байта Тип метода: эта структура используется для представления типа метода и состоит из индекса в таблице констант. 7 17 4 байта Динамический: используется для указания динамически вычисляемой константы, полученной путем вызова метода загрузки. 11 18 4 байта InvokeDynamic: используется инструкцией invokedynamic для указания метода загрузки, имени динамического вызова, типов аргументов и возвращаемого значения вызова и, опционально, последовательности дополнительных констант, называемых статическими аргументами метода загрузки. 7 19 2 байта Модуль: используется для идентификации модуля. 9 20 2 байта Пакет: используется для идентификации пакета, экспортируемого или открываемого модулем. 9
Существует только два целочисленных типа констант: integer и long. Другие целочисленные типы, встречающиеся в языке высокого уровня, такие как boolean, byte и short, должны быть представлены как целочисленная константа. Имена классов в Java, когда они полностью квалифицированы, традиционно разделяются точками, например, "java.lang.Object". Однако в константах ссылок на классы нижнего уровня используется внутренняя форма со слэшами, например, "java/lang/Object". Строки Unicode, несмотря на название "UTF-8 string", на самом деле не кодируются в соответствии со стандартом Unicode, хотя и похожи на него. Существует две отличия (см. UTF-8 для полного обсуждения). Во-первых, кодовая точка U+0000 кодируется как двухбайтовая последовательность C0 80 (в шестнадцатеричном формате) вместо стандартной однобайтовой кодировки 00. Во-вторых, дополнительные символы (те, что находятся за пределами BMP по адресу U+10000 и выше) кодируются с использованием конструкции пар суррогатов, аналогичной UTF-16, а не кодируются напрямую с использованием UTF-8. В этом случае каждый из двух суррогатов кодируется отдельно в UTF-8. Например, U+1D11E кодируется как шестибайтовая последовательность ED A0 B4 ED B4 9E, а не как правильная четырехбайтовая кодировка UTF-8 F0 9D 84 9E.