Programmēšana

Bite koda pamati

Laipni lūdzam citā "Under The Hood" daļā. Šī sleja ļauj Java izstrādātājiem ieskatu, kas notiek zem viņu darbojošajām Java programmām. Šī mēneša rakstā sākotnēji aplūkots Java virtuālās mašīnas (JVM) baitu kodu instrukciju kopums. Raksts aptver primitīvos tipus, kurus darbina baitkodi, baitkodi, kas pārveido starp tipiem, un baitkodi, kas darbojas ar skursteni. Turpmākajos rakstos tiks apspriesti citi baitkodu saimes pārstāvji.

Baitkoda formāts

Bite kodi ir Java virtuālās mašīnas mašīnvaloda. Kad JVM ielādē klases failu, tā saņem vienu baitkodu straumi katrai klases metodei. Baitkodu plūsmas tiek glabātas JVM metodes apgabalā. Metodes baitkodi tiek izpildīti, kad šī metode tiek izsaukta programmas palaišanas laikā. Tos var izpildīt, veicot intepretāciju, apkopojot tieši laikā vai izmantojot jebkuru citu tehniku, kuru izvēlējies konkrēta JVM dizainers.

Metodes baitkoda straume ir Java virtuālās mašīnas instrukciju secība. Katra instrukcija sastāv no viena baita opkods kam seko nulle vai vairāk operandi. Opkods norāda veicamo darbību. Ja pirms JVM var veikt darbību, ir nepieciešama vairāk informācijas, šī informācija tiek kodēta vienā vai vairākos operandos, kas tūlīt seko opkodam.

Katram opcode veidam ir mnemonisks. Tipiskajā asamblejas valodas stilā Java baitkodu straumes var attēlot ar to mnemotiku, kam seko jebkuras operanda vērtības. Piemēram, šādu baitkodu plūsmu var izjaukt mnemos:

Bite koda straume: 03 3b 84 00 01 1a 05 68 3b a7 ff f9 // Demontāža: iconst_0 // 03 istore_0 // 3b iinc 0, 1 // 84 00 01 iload_0 // 1a iconst_2 // 05 imul // 68 istore_0 // 3b goto -7 // a7 ff f9 

Baitkoda instrukciju kopa tika veidota tā, lai tā būtu kompakta. Visas instrukcijas, izņemot divas, kas attiecas uz lēcienu galdā, ir saskaņotas ar baitu robežām. Kopējais opkodu skaits ir pietiekami mazs, lai opkodi aizņemtu tikai vienu baitu. Tas palīdz samazināt klases failu lielumu, kas, iespējams, pārvietojas pa tīkliem, pirms JVM ielādē. Tas arī palīdz mazināt JVM ieviešanas apjomu.

Visa aprēķināšana JVM centrā ir kaudze. Tā kā JVM nav reģistru nejaušu vērtību glabāšanai, viss ir jānovieto uz kaudzes, pirms to var izmantot aprēķinos. Tāpēc Bytecode instrukcijas galvenokārt darbojas ar skursteni. Piemēram, iepriekšējā baitkoda secībā vietējais mainīgais tiek reizināts ar diviem, vispirms nospiežot vietējo mainīgo uz kaudzes ar iload_0 instrukciju, pēc tam nospiežot divus uz kaudzes ar iconst_2. Pēc tam, kad abi veseli skaitļi ir iestumti uz kaudzes, imul instrukcija efektīvi atmet divus veselos skaitļus no kaudzes, reizina tos un izspiež rezultātu atpakaļ uz kaudzes. Rezultāts tiek izvadīts no kaudzes augšdaļas un tiek saglabāts atpakaļ vietējā mainīgajā istore_0 instrukcija. JVM tika veidots kā mašīna, kas balstīta uz kaudzēm, nevis uz reģistru, lai atvieglotu efektīvu ieviešanu reģistros nabadzīgajās arhitektūrās, piemēram, Intel 486.

Primitīvie veidi

JVM atbalsta septiņus primitīvus datu tipus. Java programmētāji var deklarēt un izmantot šo datu tipu mainīgos, un Java baitkodi darbojas ar šiem datu tipiem. Septiņi primitīvie veidi ir uzskaitīti šajā tabulā:

TipsDefinīcija
baitsviens baits parakstīja divu papildinājumu veselu skaitli
īssdivbaiti parakstīja divu papildinājumu veselu skaitli
int4 baiti parakstīja divu papildinājumu veselu skaitli
ilgi8 baiti parakstīja divu papildinājumu veselu skaitli
peldēt4 baitu IEEE 754 vienas precizitātes pludiņš
dubultā8 baitu IEEE 754 dubultprecizitātes pludiņš
char2 baitu neparakstīts Unicode raksturs

Primitīvie tipi parādās kā operandi baitkodu plūsmās. Visi primitīvie tipi, kas aizņem vairāk nekā 1 baitu, baitkodu straumē tiek glabāti lielā endiāna secībā, kas nozīmē, ka augstākas kārtas baiti ir pirms zemākas kārtas baitiem. Piemēram, lai pastumtu konstantu vērtību 256 (heks. 0100) uz kaudzes, jūs izmantojat sipush opkods, kam seko īss operands. Īsā baitkodu straumē, kas parādīta zemāk, tiek parādīts kā "01 00", jo JVM ir liels endijs. Ja JVM būtu mazs endijs, saīsinājums parādīsies kā "00 01".

 Bite koda plūsma: 17 01 00 // Demontāža: sipush 256; 17 01 00 

Java opkodi parasti norāda to operandu veidu. Tas ļauj operandiem būt tikai pašiem, bez vajadzības identificēt viņu tipu JVM. Piemēram, tā vietā, lai būtu viens opkods, kas vietējo mainīgo iespiež kaudzē, JVM ir vairāki. Opcodes iload, krava, slodze, un ielādēt uz kaudzes nospiediet attiecīgi int, long, float un double lokālos mainīgos.

Konstantu spiešana uz kaudzes

Daudzi opkodi uzstāda konstantes uz kaudzes. Opcodes norāda nemainīgo vērtību, kas jāstumj trīs dažādos veidos. Pastāvīgā vērtība ir vai nu netieša pašā opkodā, tā seko opkodam baitu koda straumē kā operands vai tiek ņemta no nemainīgā kopas.

Daži opkodi paši par sevi norāda virzienu un nemainīgu vērtību. Piemēram, iconst_1 opcode liek JVM virzīt veselu skaitli vienu vērtību. Šādi baitkodi ir definēti dažiem dažāda veida bieži virzītiem skaitļiem. Šīs instrukcijas aizņem tikai 1 baitu baitu kodu straumē. Tie palielina bytecode izpildes efektivitāti un samazina bytecode plūsmu lielumu. Opcodes, kas piespiež ints un pludina, ir parādīti šajā tabulā:

OpcodeOperands (-i)Apraksts
iconst_m1(nav)nospiež int -1 uz kaudzes
iconst_0(nav)nospiež int 0 uz kaudzes
iconst_1(nav)nospiež int 1 uz kaudzes
iconst_2(nav)nospiež int 2 uz kaudzes
iconst_3(nav)nospiež int 3 uz kaudzes
ikona_4(nav)nospiež int 4 uz kaudzes
iconst_5(nav)nospiež int 5 uz kaudzes
fconst_0(nav)nospiež pludiņu 0 uz kaudzes
fconst_1(nav)nospiež pludiņu 1 uz kaudzes
fconst_2(nav)nospiež pludiņu 2 uz kaudzes

Iepriekšējā tabulā redzamie opkodi nospiež ints un peld, kas ir 32 bitu vērtības. Katrs Java kaudzes slots ir 32 bitu plats. Tāpēc katru reizi, kad int vai pludiņš tiek stumts uz kaudzes, tas aizņem vienu slotu.

Nākamajā tabulā redzamie opkodi spiež garus un dubultus. Garās un dubultās vērtības aizņem 64 bitus. Katru reizi, kad garu vai dubultu tiek nospiests uz kaudzes, tā vērtība aizņem divas vietas uz kaudzes. Opcodes, kas norāda noteiktu garu vai dubultu virzāmo vērtību, ir redzamas šajā tabulā:

OpcodeOperands (-i)Apraksts
lconst_0(nav)nospiež garo 0 uz kaudzes
lconst_1(nav)nospiež garo 1 uz kaudzes
dconst_0(nav)nospiež dubulto 0 uz kaudzes
dconst_1(nav)nospiež dubultu 1 uz kaudzes

Viens cits opkods nospiež netiešo nemainīgo vērtību uz kaudzes. The aconst_null opkods, kas parādīts nākamajā tabulā, uz kaudzīti nospiež nulles objekta atsauci. Objekta atsauces formāts ir atkarīgs no JVM ieviešanas. Objekta atsauce kaut kādā veidā atsauksies uz Java objektu atkritumu savāktajā kaudzē. Nulles objekta atsauce norāda, ka objekta atsauces mainīgais pašlaik neattiecas uz nevienu derīgu objektu. The aconst_null opcode tiek izmantots nulles piešķiršanas procesā objekta atsauces mainīgajam.

OpcodeOperands (-i)Apraksts
aconst_null(nav)nospiež nulles objekta atsauci uz kaudzīti

Divi opkodi norāda pastāvīgo spiedienu ar operandu, kas tūlīt seko opkodam. Šie opcodes, kas parādīti nākamajā tabulā, tiek izmantoti, lai virzītu veselu skaitļu konstantes, kas ir derīgā diapazona baitu vai īsu tipu diapazonā. Baits vai īss, kas seko opkodam, tiek paplašināts līdz int, pirms tas tiek stumts uz kaudzes, jo katrs Java kaudzes slots ir 32 bitu plats. Operācijas ar baitiem un šortiem, kas ir iestumti uz kaudzes, faktiski tiek veiktas pēc to ekvivalentiem.

OpcodeOperands (-i)Apraksts
bipushbaits1paplašina 1. baitu (baita veidu) līdz int un nospiež to uz kaudzes
sipushbaits1, baits2izpleš baitu1, baitu2 (īsu tipu) līdz int un nospiež tos uz kaudzes

Trīs opkodi izstumj konstantes no nemainīgā baseina. Visas ar klasi saistītās konstantes, piemēram, galīgo mainīgo vērtības, tiek saglabātas klases nemainīgajā krājumā. Opkodos, kas izspiež konstantes no nemainīgā kopas, ir operandi, kas norāda, kuru konstanti virzīt, norādot nemainīgu kopas indeksu. Java virtuālā mašīna meklēs konstanti, kas dota indeksam, noteiks konstantes tipu un nospiedīs to uz kaudzes.

Pastāvīgā kopas indekss ir neparakstīta vērtība, kas tūlīt seko opkodam baitu kodu straumē. Opcodes lcd1 un lcd2 nospiediet uz kaudzes 32 bitu priekšmetu, piemēram, int vai float. Atšķirība starp lcd1 un lcd2 vai tas ir lcd1 var atsaukties tikai uz nemainīgām baseina vietām no viena līdz 255, jo tā indekss ir tikai 1 baits. (Pastāvīga baseina atrašanās vieta nulle nav izmantota.) lcd2 ir 2 baitu indekss, tāpēc tas var atsaukties uz jebkuru nemainīgu baseina atrašanās vietu. lcd2w ir arī 2 baitu indekss, un to izmanto, lai atsauktos uz jebkuru nemainīgu baseina vietu, kurā ir garš vai dubults, kas aizņem 64 bitus. Opkodi, kas izspiež konstantes no nemainīgā kopas, ir parādīti šajā tabulā:

OpcodeOperands (-i)Apraksts
ldc1rādītājbaits1izspiež kaudzē 32 bitu konstante_pool ievadi, ko norādījis indexbyte1
ldc2indexbyte1, indexbyte2izspiež kaudzē 32 bitu konstante_pool ievadi, ko norādījuši indexbyte1, indexbyte2
ldc2windexbyte1, indexbyte2izspiež kaudzē 64 bitu konstante_pool ievadi, ko norādījuši indexbyte1, indexbyte2

Vietējo mainīgo spiešana uz kaudzes

Vietējie mainīgie tiek glabāti īpašā kaudzes rāmja sadaļā. Steka rāmis ir tā kaudzes daļa, kuru izmanto pašreiz izpildītā metode. Katru kaudzes rāmi veido trīs sadaļas - lokālie mainīgie, izpildes vide un operanda kaudze. Vietējā mainīgā virzīšana uz kaudzīti faktiski nozīmē vērtības pārvietošanu no kaudzes rāmja vietējo mainīgo sadaļas uz operanda sadaļu. Pašlaik izpildītās metodes operanda sadaļa vienmēr ir kaudzes augšdaļa, tāpēc vērtības nospiešana uz pašreizējā kaudzes rāmja operanda sadaļu ir tāda pati kā vērtības nospiešana kaudzes augšdaļā.

Java kaudze ir pēdējā-pirmā, pirmā-ārējā 32 bitu slotu kaudze. Tā kā katrs kaudzes slots aizņem 32 bitus, visi lokālie mainīgie aizņem vismaz 32 bitus. Garie un dubultie lokālie mainīgie, kas ir 64 bitu lielumi, aizņem divus slotus uz kaudzes. Vietējie mainīgie ar baitu vai īsu tipu tiek saglabāti kā int tipa lokālie mainīgie, bet ar vērtību, kas ir derīga mazākajam tipam. Piemēram, int lokālajā mainīgajā, kas attēlo baita tipu, vienmēr būs baitam derīga vērtība (-128 <= vērtība <= 127).

Katram metodes lokālajam mainīgajam ir unikāls indekss. Metodes kaudzes rāmja lokālo mainīgo sadaļu var uzskatīt par 32 bitu slotu masīvu, no kuriem katru adresē masīva indekss. Uz garajiem vai dubultajiem lokālajiem mainīgajiem lielumiem, kas aizņem divus laika nišus, atsaucas zemākais no diviem laika nišu indeksiem. Piemēram, uz dubultnieku, kas aizņem otro un trešo vietu, atsaucas indekss divi.

Pastāv vairāki opkodi, kas vietējos mainīgos izstumj un uzpeld operanda kaudzē. Ir definēti daži opkodi, kas netieši norāda uz parasti izmantoto lokālo mainīgo pozīciju. Piemēram, iload_0 ielādē int lokālo mainīgo nulles pozīcijā. Citus lokālos mainīgos uz steku iestumj opkods, kas vietējā mainīgā indeksu ņem no pirmā baita, kas seko opkodam. The iload instrukcija ir šāda veida opcode piemērs. Pirmais baits, kas seko iload tiek interpretēts kā neparakstīts 8 bitu indekss, kas attiecas uz vietējo mainīgo.

Neparakstīti 8 bitu lokālo mainīgo indeksi, piemēram, tas, kas seko iload instrukciju, ierobežojiet lokālo mainīgo skaitu metodē līdz 256. Atsevišķa instrukcija, ko sauc plašs, var pagarināt 8 bitu indeksu vēl par 8 bitiem. Tas vietējo mainīgo robežu paaugstina līdz 64 kilobaitiem. The plašs opkodam seko 8 bitu operands. The plašs opkods un tā operands var būt pirms instrukcijas, piemēram, iload, kas prasa 8 bitu neparakstītu vietējo mainīgo indeksu. JVM apvieno operētājsistēmas 8 bitu operandu plašs instrukcija ar 8 bitu operandu iload instrukcija, lai iegūtu 16 bitu neparakstītu lokālo mainīgo indeksu.

Opcodes, kas izspiež int un peld lokālos mainīgos uz kaudzes, ir parādīti šajā tabulā:

OpcodeOperands (-i)Apraksts
iloadvindexnospiež int no lokālās mainīgās pozīcijas vindex
iload_0(nav)nospiež int no vietējā mainīgā stāvokļa nulle
iload_1(nav)nospiež int no lokālā mainīgā stāvokļa viens
iload_2(nav)nospiež int no vietējā mainīgā stāvokļa divi
iload_3(nav)nospiež int no vietējā mainīgā stāvokļa trīs
slodzevindexnospiež pludiņu no vietējā mainīgā stāvokļa vindex
fload_0(nav)nospiež pludiņu no nulles lokālā mainīgā stāvokļa
fload_1(nav)spiež pludiņu no lokālā mainīgā stāvokļa
fload_2(nav)nospiež pludiņu no vietējā mainīgā stāvokļa diviem
fload_3(nav)nospiež pludiņu no vietējā mainīgā stāvokļa trīs

Nākamajā tabulā ir parādītas instrukcijas, ar kurām vietējie mainīgie garā un dubultā veidā tiek virzīti uz kaudzīti. Šīs instrukcijas pārvieto 64 bitus no kaudzes rāmja lokālās mainīgās daļas uz operanda sadaļu.

$config[zx-auto] not found$config[zx-overlay] not found