SectionsEdit
Er zijn 10 basissecties in de Java class file structuur:
- Magic Number: 0xCAFEBABE
- Version of Class File Format: de minor en major versies van het class file
- Constant Pool: Pool van constanten voor de klasse
- Access Flags: bijvoorbeeld of de klasse abstract is, statisch, enz.
- Deze klasse: De naam van de huidige klasse
- Superklasse: De naam van de superklasse
- Interfaces: Eventuele interfaces in de klasse
- Velden: Eventuele velden in de klasse
- Methoden: Alle methoden in de klasse
- Attributen: Eventuele attributen van de klasse (bijvoorbeeld de naam van het bronbestand, etc.)
Magic NumberEdit
Klasse bestanden worden geïdentificeerd door de volgende 4 byte header (in hexadecimaal): CA FE BA BE
(de eerste 4 regels in de tabel hieronder). De geschiedenis van dit magische getal werd uitgelegd door James Gosling, die verwees naar een restaurant in Palo Alto:
“We gingen vroeger lunchen in een plaats die St Michael’s Alley heette. Volgens een plaatselijke legende trad daar in een ver verleden de Grateful Dead op, voordat ze groot werden. Het was een behoorlijk funky plek die zeker een Grateful Dead Kinda Place was. Toen Jerry stierf, hebben ze er zelfs een klein boeddhistisch heiligdom neergezet. Toen we daar kwamen, noemden we het Cafe Dead. Ergens in de loop van de tijd werd het me duidelijk dat dit een HEX nummer was. Ik was wat bestandsformaat code aan het vernieuwen en had een paar magische nummers nodig: een voor het persistent object bestand, en een voor classes. Ik gebruikte CAFEDEAD voor het object bestandsformaat, en tijdens het zoeken naar 4 karakter hex woorden die pasten na “CAFE” (het leek een goed thema te zijn) kwam ik BABE tegen en besloot het te gebruiken. Op dat moment leek het niet erg belangrijk of voorbestemd om ergens anders heen te gaan dan de prullenbak van de geschiedenis. Dus CAFEBABE werd het klasse bestandsformaat, en CAFEDEAD was het persistente object formaat. Maar de persistente object faciliteit verdween, en daarmee ook het gebruik van CAFEDEAD – het werd uiteindelijk vervangen door RMI.
Algemene layoutEdit
Omdat het klassebestand items van variabele grootte bevat en niet ook ingebedde bestandsoffsets (of pointers) bevat, wordt het typisch sequentieel geparseerd, vanaf de eerste byte naar het einde toe. Op het laagste niveau wordt het bestandsformaat beschreven in termen van een paar fundamentele datatypes:
- u1: een niet-ondertekend 8-bit geheel getal
- u2: een niet-ondertekend 16-bit geheel getal in big-endian bytevolgorde
- u4: een niet-ondertekend 32-bit geheel getal in big-endian bytevolgorde
- tabel: een array van items van variabele lengte van een of ander type. Het aantal items in de tabel wordt geïdentificeerd door een voorafgaand telgetal (de telling is een u2), maar de grootte in bytes van de tabel kan alleen worden bepaald door elk van de items te onderzoeken.
Enkele van deze fundamentele types worden vervolgens geherinterpreteerd als waarden van een hoger niveau (zoals strings of floating-point getallen), afhankelijk van de context.Er is geen handhaving van woord uitlijning, en dus worden er nooit padding bytes gebruikt.De algemene lay-out van het klassebestand is zoals weergegeven in de volgende tabel.
byte offset | size | type of waarde | description |
---|---|---|---|
0 | 4 bytes | u1 = 0xCA hex |
magisch getal (CAFEBABE) gebruikt om bestand te identificeren als zijnde conform de class file format |
1 | u1 = 0xFE hex |
||
2 | u1 = 0xBA hex |
||
3 | u1 = 0xBE hex |
||
4 | 2 bytes | u2 | minor versienummer van de klassebestandsindeling die wordt gebruikt |
5 | |||
6 | 2 bytes | u2 | major versienummer van de klassebestandsindeling die wordt gebruikt.
Java SE 17 = 61 (0x3D hex), |
7 | |||
8 | 2 bytes | u2 | constant pool count, aantal entries in de volgende constant pool tabel. Deze telling is minstens één groter dan het werkelijke aantal entries; zie de volgende discussie. |
9 | |||
10 | cpsize (variabel) | table | constantpooltabel, een array van constante pool entries van variabele grootte, die items bevat zoals letterlijke getallen, strings, en verwijzingen naar klassen of methoden. Geïndexeerd beginnend bij 1, met (constant pool count – 1) aantal entries in totaal (zie noot). |
… | |||
… | |||
… | |||
10+cpsize | 2 bytes | u2 | access flags, een bitmask |
11+cpsize | |||
12+cpsize | 2 bytes | u2 | identificeert deze klasse, index in de constante pool naar een “Class”-type item |
13+cpsize | |||
14+cpsize | 2 bytes | u2 | identificeert de superklasse, index in de constante pool naar een “Class”-type entry |
15+cpsize | |||
16+cpsize | 2 bytes | u2 | interfacetelling, aantal ingangen in de volgende interfacetabel |
17+cpsize | |||
18+cpsize | isize (variabel) | table | interfacetabel: een array met variabele lengte van constante poolindexen die de interfaces beschrijven die door deze klasse worden geïmplementeerd |
… | |||
… | |||
… | |||
18+cpsize+isize | 2 bytes | u2 | veldtelling, aantal ingangen in de volgende veldentabel |
19+cpsize+isize | |||
20+cpsize+isize | fsize (variabel) | table | veldentabel, array van velden met variabele lengte
elk element is een field_info-structuur gedefinieerd in https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.5 |
… | |||
… | |||
… | |||
20+cpsize+isize+fsize | 2 bytes | u2 | method count, aantal items in de volgende methodetabel |
21+cpsize+isize+fsize | |||
22+cpsize+isize+fsize | msize (variabel) | table | methodetabel, array van methodes met variabele lengte
elk element is een method_info-structuur gedefinieerd in https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.6 |
… | |||
… | |||
… | |||
22+cpsize+isize+fsize+msize | 2 bytes | u2 | attribuutaantal, aantal items in de volgende attribuuttabel |
23+cpsize+isize+fsize+msize | |||
24+cpsize+isize+fsize+msize | asize (variabel) | table | attribuuttabel, matrix van attributen met variabele lengte
elk element is een attribuut_infostructuur gedefinieerd in https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7 |
… | |||
… | |||
… |
Voorstelling in een C-achtige programmeertaalEdit
Omdat C geen arrays met meerdere variabele lengtes binnen een struct ondersteunt, zal de onderstaande code niet compileren en dient deze slechts als demonstratie.
struct Class_File_Format { u4 magic_number; u2 minor_version; u2 major_version; u2 constant_pool_count; cp_info constant_pool; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces; u2 fields_count; field_info fields; u2 methods_count; method_info methods; u2 attributes_count; attribute_info attributes;}
De constante poolEdit
De constante pool tabel is waar de meeste letterlijke constante waarden worden opgeslagen. Dit omvat waarden zoals getallen van alle soorten, strings, identifier namen, verwijzingen naar klassen en methoden, en type descriptors. Alle indexen, of verwijzingen, naar specifieke constanten in de constante pool tabel worden gegeven door 16-bits (type u2) getallen, waarbij index waarde 1 verwijst naar de eerste constante in de tabel (index waarde 0 is ongeldig).
Door historische keuzes gemaakt tijdens de ontwikkeling van het bestandsformaat, is het aantal constanten in de constante pool tabel in werkelijkheid niet hetzelfde als de constante pool telling die aan de tabel voorafgaat. Ten eerste is de tabel geïndexeerd beginnend bij 1 (in plaats van 0), maar de telling moet eigenlijk worden geïnterpreteerd als de maximum index plus één. Bovendien, twee soorten constanten (longs en doubles) nemen twee opeenvolgende slots in de tabel, hoewel de tweede zo’n slot een phantom index is die nooit direct wordt gebruikt.
Het type van elk item (constante) in de constante pool wordt geïdentificeerd door een initiële byte tag. Het aantal bytes na deze tag en hun interpretatie zijn dan afhankelijk van de tag waarde. De geldige constante types en hun tag waarden zijn:
Tag byte | Aanvullende bytes | Beschrijving van constante | Versie geïntroduceerd |
---|---|---|---|
1 | 2+x bytes (variabele) |
UTF-8 (Unicode) string: een tekenreeks voorafgegaan door een 16-bits getal (type u2) dat het aantal bytes aangeeft in de gecodeerde tekenreeks die onmiddellijk volgt (en dat verschillend kan zijn van het aantal tekens). Merk op dat de gebruikte codering eigenlijk geen UTF-8 is, maar een lichte wijziging van de Unicode-standaardcoderingsvorm. | 1.0.2 |
3 | 4 bytes | Integer: een 32-bits two’s complement getal in big-endian formaat | 1.0.2 |
4 | 4 bytes | Float: een 32-bits single-precision IEEE 754 floating-point getal | 1.0.2 |
5 | 8 bytes | Long: een ondertekend 64-bit two’s complement getal in big-endian formaat (neemt twee slots in de constant pool tabel in) | 1.0.2 |
6 | 8 bytes | Double: een 64-bit double-precision IEEE 754 floating-point getal (neemt twee slots in de tabel met constante pool) | 1.0.2 |
7 | 2 bytes | Class reference: een index binnen de constant pool naar een UTF-8 string die de volledig gekwalificeerde class name bevat (in intern formaat) (big-endian) | 1.0.2 |
8 | 2 bytes | String reference: een index binnen de constante pool naar een UTF-8 string (ook big-endian) | 1.0.2 |
9 | 4 bytes | Field reference: twee indexen binnen de constant pool, de eerste die naar een Class reference wijst, de tweede naar een Name and Type descriptor. (big-endian) | 1.0.2 |
10 | 4 bytes | Methodeverwijzing: twee indexen binnen de constante pool, de eerste die wijst naar een Klasse-verwijzing, de tweede naar een Naam- en Type-descriptor. (big-endian) | 1.0.2 |
11 | 4 bytes | Interface method reference: twee indexen binnen de constante pool, de eerste die wijst naar een Klasse-verwijzing, de tweede naar een Name en Type descriptor. (big-endian) | 1.0.2 |
12 | 4 bytes | Naam- en type-descriptor: twee indexen naar UTF-8-strings binnen de constante pool, de eerste die een naam (identifier) weergeeft, de tweede een speciaal gecodeerde type-descriptor. | 1.0.2 |
15 | 3 bytes | Method handle: deze structuur wordt gebruikt om een method handle weer te geven en bestaat uit één byte van de type-descriptor, gevolgd door een index binnen de constante pool. | 7 |
16 | 2 bytes | Method type: deze structuur wordt gebruikt om een methodetype weer te geven, en bestaat uit een index binnen de constante pool. | 7 |
17 | 4 bytes | Dynamisch: deze wordt gebruikt om een dynamisch berekende constante op te geven die wordt geproduceerd door aanroeping van een bootstrap-methode. | 11 |
18 | 4 bytes | InvokeDynamic: dit wordt gebruikt door een invokedynamic-instructie om een bootstrap-methode te specificeren, de dynamische invocatienaam, de argument- en return-typen van de aanroep, en optioneel, een reeks aanvullende constanten die statische argumenten voor de bootstrap-methode worden genoemd. | 7 |
19 | 2 bytes | Module: dit wordt gebruikt om een module te identificeren. | 9 |
20 | 2 bytes | Package: dit wordt gebruikt om een pakket aan te duiden dat door een module wordt geëxporteerd of geopend. | 9 |
Er zijn slechts twee integrale constante types, integer en long. Andere integrale typen die in de hogere taal voorkomen, zoals boolean, byte en short, moeten worden weergegeven als een integer constante.
Klassenamen in Java worden, wanneer ze volledig gekwalificeerd zijn, traditioneel door punten gescheiden, zoals “java.lang.Object”. Maar binnen de klasse-referentieconstanten op laag niveau, verschijnt een interne vorm die in plaats daarvan schuine strepen gebruikt, zoals “java/lang/Object”.
De Unicode strings, ondanks de bijnaam “UTF-8 string”, zijn in feite niet gecodeerd volgens de Unicode standaard, hoewel het er wel op lijkt. Er zijn twee verschillen (zie UTF-8 voor een volledige discussie). Het eerste is dat het codepunt U+0000 wordt gecodeerd als de twee-byte sequentie C0 80
(in hex) in plaats van de standaard enkel-byte codering 00
. Het tweede verschil is dat aanvullende tekens (die buiten de BMP op U+10000 en hoger) worden gecodeerd met behulp van een surrogaat-paar constructie vergelijkbaar met UTF-16 in plaats van direct te worden gecodeerd met UTF-8. In dit geval wordt elk van de twee surrogaten apart gecodeerd in UTF-8. Bijvoorbeeld, U+1D11E wordt gecodeerd als de 6-byte sequentie ED A0 B4 ED B4 9E
, in plaats van de correcte 4-byte UTF-8 codering van F0 9D 84 9E
.