Sveiki atvykę į kitą Po gaubtu. Šis stulpelis leidžia „Java“ kūrėjams pažvelgti į paslaptingus mechanizmus, spustelėjusius ir sukančius po veikiančiomis „Java“ programomis. Šio mėnesio straipsnyje tęsiama „Java“ virtualiosios mašinos (JVM) baitų kodo instrukcijų rinkinio aptarimas. Pagrindinis dėmesys skiriamas JVM elgesiui pagaliau
šiems sakiniams aktualūs sakiniai ir jų kodai.
Galiausiai: kažkas, kuo džiaugtis
Kai „Java“ virtuali mašina vykdo „Java“ programą atstovaujančius baitų kodus, ji gali išeiti iš kodo bloko - sakinių tarp dviejų derančių garbanotų petnešėlių - vienu iš kelių būdų. Viena vertus, JVM paprasčiausiai galėjo įvykdyti kodo bloko uždaromąjį garbanotąjį petnešą. Arba jis gali susidurti su pertrauka, tęsti arba grąžinti sakinį, dėl kurio jis iššoka iš kodo bloko iš kažkur bloko viduryje. Galiausiai gali būti padaryta išimtis, dėl kurios JVM pereina prie atitinkančios sugavimo sąlygos arba, jei nėra atitinkančios sugavimo sąlygos, nutraukia giją. Kai šie potencialūs išėjimo taškai yra viename kodo bloke, pageidautina turėti paprastą būdą išreikšti, kad kažkas įvyko, kad ir kaip būtų išjungtas kodo blokas. Java kalboje toks noras išreiškiamas a pabandyk pagaliau
sąlyga.
Norėdami naudoti a pabandyk pagaliau
sąlyga:
pridėti a
bandyti
blokuoti kodą, kuriame yra keli išėjimo taškai, irįdėti a
pagaliau
blokuoti kodą, kuris turi atsitikti, kad ir kaipbandyti
užblokuotas.
Pavyzdžiui:
pabandykite {// Kodo blokas su keliais išėjimo taškais} galiausiai {// Kodo blokas, kuris visada vykdomas, kai bandymo blokas yra išjungtas, // nesvarbu, kaip bandomasis blokas yra išeinamas}
Jei tokių turite pagauti
su sąlyga susijusios sąlygos bandyti
blokuoti, turite įdėti pagaliau
sąlyga po visų pagauti
sąlygos, kaip nurodyta:
pabandykite {// Blokuoti kodą su keliais išėjimo taškais} gaudyti (Cold e) {System.out.println ("Caught cold!"); } gaudyti (APopFly e) {System.out.println ("Pagavo popmuziką!"); } pagauti (SomeonesEye e) {System.out.println ("Patraukė kieno nors akį!"); } pagaliau {// Kodo blokas, kuris visada vykdomas, kai bandomasis blokas yra išjungtas, // nesvarbu, kaip bandomasis blokas yra išjungtas. System.out.println ("Ar tai yra kuo džiaugtis?"); }
Jei vykdant kodą per bandyti
bloką, išmetama išimtis, kurią tvarko a pagauti
sąlyga, susijusi su bandyti
blokas, pagaliau
sąlyga bus įvykdyta po pagauti
sąlyga. Pavyzdžiui, jei a Šalta
išimtis metama vykdant pareiškimus (neparodyti) bandyti
aukščiau esančiame laukelyje šis tekstas bus įrašytas į standartinę išvestį:
Pagavo šaltas! Ar tai yra kuo džiaugtis?
Pabandykite pagaliau baitų koduose
Baitų koduose pagaliau
sąlygos metodo metu veikia kaip miniatiūrinės paprogramės. Kiekviename išėjimo taške, esančiame a bandyti
blokas ir su juo susijęs pagauti
sąlygos, miniatiūrinis paprogramė, atitinkanti pagaliau
vadinama sąlyga. Po to, kai pagaliau
sąlyga baigiama - tol, kol ji baigiama vykdant paskutinį pagaliau
sąlyga, o ne metant išimtį ar vykdant grąžą, tęsti ar pertraukti - grįžta pati miniatiūrinė paprogramė. Vykdymas tęsiasi tiesiai už taško, kuriame pirmiausia buvo iškviestas miniatiūrinis paprogramė, taigi bandyti
bloką galima išeiti tinkamu būdu.
Opcode, dėl kurio JVM pereina į miniatiūrinį paprogramę, yra jsr instrukcija. jsr instrukcijai reikalingas dviejų baitų operandas, poslinkis nuo operacijos vietos jsr instrukcija, kur prasideda miniatiūrinis paprogramė. Antrasis variantas jsr instrukcija yra jsr_w, atliekanti tą pačią funkciją kaip jsr bet užima platų (keturių baitų) operandą. Kai JVM susiduria a jsr arba jsr_w instrukciją, jis stumia grįžimo adresą į kaminą, tada tęsia vykdymą miniatiūrinio paprogramės pradžioje. Grąžinimo adresas yra baito kodo, iš karto po jo, poslinkis jsr arba jsr_w instrukcija ir jos operandai.
Užbaigus miniatiūrinį paprogramę, jis iškviečia ret instrukcija, kuri grįžta iš paprogramės. ret instrukcija ima vieną operandą, indeksą į vietinius kintamuosius, kur saugomas grąžinimo adresas. Opcodes, su kuriais susiduriama pagaliau
sąlygos yra apibendrintos šioje lentelėje:
„Opcode“ | Operandas (-ai) | apibūdinimas |
---|---|---|
jsr | branchbyte1, branchbyte2 | stumia grįžimo adresą, šakojasi atskaityti |
jsr_w | branchbyte1, branchbyte2, branchbyte3, šakos baitas4 | stumia grįžimo adresą, išsišakoja į platų poslinkį |
ret | indeksas | grįžta į adresą, išsaugotą vietiniame kintamųjų rodyklėje |
Nepainiokite miniatiūrinio paprogramės su „Java“ metodu. „Java“ metodai naudoja kitokį instrukcijų rinkinį. Tokios instrukcijos kaip invokevirtualus arba invokenonvirtual sukelti „Java“ metodą ir tokias instrukcijas kaip grįžti, grįžimasarba grįšiu sukelti „Java“ metodo grįžimą. jsr instrukcija nesukelia Java metodo. Vietoj to, tai sukelia tą patį metodą pereinant prie kito opcode. Taip pat ret instrukcija negrįžta iš metodo; veikiau grįžta prie opkodo tuo pačiu metodu, kuris iškart seka paskambinimą jsr instrukcija ir jos operandai. Baitų kodai, įgyvendinantys a pagaliau
sakinys yra vadinamas miniatiūriniu paprogramiu, nes jie veikia kaip mažas paprogramė vieno metodo baitų kodų sraute.
Galite pagalvoti, kad ret instrukcija turėtų iššokti grąžinimo adresą iš kamino, nes būtent ten jį nustūmė jsr instrukcija. Bet taip nėra. Vietoj to, kiekvienos paprogramės pradžioje grąžinimo adresas iššoka nuo kamino viršaus ir saugomas vietiniame kintamajame - tame pačiame vietiniame kintamajame, iš kurio ret instrukcija vėliau gauna. Šis asimetriškas darbo su grįžimo adresu būdas yra būtinas, nes galiausiai sąlygos (taigi ir miniatiūrinės paprogramės) gali sukelti išimčių arba įtraukti grįžti
, pertrauka
arba Tęsti
pareiškimus. Dėl šios galimybės papildomas grąžinimo adresas, kurį jsr instrukcija turi būti nedelsiant pašalinta iš kamino, todėl jos vis tiek nebus, jei pagaliau
sąlyga išeina su a pertrauka
, Tęsti
, grįžti
, arba išmesta išimtis. Todėl grąžinimo adresas bet kurio pradžioje saugomas vietiniame kintamajame pagaliau
miniatiūrinio punkto paprogramė.
Kaip iliustraciją, apsvarstykite šį kodą, kuriame yra a pagaliau
sąlyga, kuri išeina su lūžio sakiniu. Šio kodo rezultatas yra tas, kad, nepriklausomai nuo metodo perduoto parametro bVal staigmenaProgramuotojas ()
, metodas grąžinamas melagingas
:
statinis loginis staigmenaTheProgrammer (boolean bVal) {while (bVal) {try {return true; } pagaliau {pertrauka; }} return false; }
Aukščiau pateiktame pavyzdyje parodyta, kodėl grąžinimo adresas turi būti saugomas vietiniame kintamajame pagaliau
sąlyga. Nes pagaliau
sąlyga išeina su pertrauka, ji niekada nevykdo ret instrukcija. Todėl JVM niekada negrįžta baigti „grįžti tiesa
"Vietoj to, jis tiesiog eina į priekį su pertrauka
ir nuleidžia žemyn už uždaromos garbanotos petnešos kol
pareiškimas. Kitas teiginys yra „grąžinti klaidingą
", būtent tai ir daro JVM.
Elgesys, kurį rodo a pagaliau
sąlyga, kuri išeina su a pertrauka
taip pat rodo pagaliau
sąlygos, kurios išeina su a grįžti
arba Tęsti
, arba metant išimtį. Jeigu pagaliau
išlyga išplaukia dėl bet kurios iš šių priežasčių, ret instrukcija pagaliau
sąlyga niekada nevykdoma. Nes ret negarantuojama, kad instrukcija bus vykdoma, ja negalima remtis norint pašalinti grąžinimo adresą iš rietuvės. Todėl grąžinimo adresas yra saugomas vietiniame kintamajame pagaliau
miniatiūrinio punkto paprogramė.
Norėdami gauti išsamų pavyzdį, apsvarstykite šį metodą, kuriame yra a bandyti
blokas su dviem išėjimo taškais. Šiame pavyzdyje abu išėjimo taškai yra grįžti
teiginiai:
static int giveMeThatOldFashionedBoolean (boolean bVal) {try {if (bVal) {grąžinti 1; } grąžinti 0; } pagaliau {System.out.println ("Pasenau."); }}
Aukščiau pateiktas metodas sudaro šiuos baitekodus:
// Pabandymo bloko baitų kodų seka: 0 iload_0 // Stumti vietinį kintamąjį 0 (arg perduotas kaip daliklis) 1 ifeq 11 // Stumti vietinį kintamąjį 1 (arg perduotas kaip dividendas) 4 iconst_1 // Push int 1 5 istore_3 // Įveskite int (1), išsaugokite 3 vietiniame kintamajame 6 jsr 24 // Pereikite prie mini paprogramės, kad gautumėte paskutinį 9 straipsnį iload_3 // Paspauskite 3 vietinį kintamąjį (1) 10 ireturn // Grįžkite int viršuje kaminas (1) 11 iconst_0 // Push int 0 12 istore_3 // Pop an int (the 0), store in local kintamasis 3 13 jsr 24 // Pereikite prie mini paprogramės, kad galiausiai galėtumėte sakyti 16 iload_3 // Push local 3 kintamasis (0) 17 grįžimas // Grįžimas int ant kamino viršaus (0) // Baito kodo seka sugavimo sąlygai, kuri užfiksuoja bet kokią išimtį //, išmestą iš bandymo bloko. 18 astore_1 // Įtraukite nuorodą į išmestą išimtį, saugokite // į 1 vietinį kintamąjį 19 jsr 24 // Peršokite į mini paprogramę, kad gautumėte paskutinį 22 punktą aload_1 // Pastumkite nuorodą (į metamą išimtį) iš // 1 kintamasis kintamasis 23 athrow // Pakartokite tą pačią išimtį // Miniatiūrinis paprogramis, įgyvendinantis galutinį bloką. 24 astore_2 // Parodykite grąžinimo adresą, išsaugokite jį 2 vietiniame kintamajame 25 getstatic # 8 // Gaukite nuorodą į java.lang.System.out 28 ldc # 1 // Išstumkite iš pastovaus telkinio 30 invokevirtual # 7 // Invoke System.out.println () 33 ret 2 // Grįžti į grįžimo adresą, saugomą 2 vietiniame kintamajame
Už bytecodes bandyti
blokas apima du jsr instrukcijas. Kitas jsr instrukcija yra pagauti
sąlyga. pagauti
sąlyga pridedama kompiliatoriaus, nes jei vykdant bandyti
blokuoti, galutinis blokas vis tiek turi būti vykdomas. Todėl pagauti
sąlyga tik remiasi miniatiūriniu paprogramiu, kuris reprezentuoja pagaliau
sąlygą, tada vėl meta tą pačią išimtį. Išimčių lentelė giveMeThatOldFashionedBoolean ()
metodas, parodytas žemiau, rodo, kad bet kokia išimtis, įmesta tarp 0 ir 17 adresų (įskaitant visus bytec kodus, kurie įgyvendina bandyti
bloką) tvarko pagauti
sąlyga, prasidedanti 18 adresu.
Išimčių lentelė: nuo tikslo tipo 0 18 18 bet kuri
Baitekodai pagaliau
sąlyga prasideda iškeliant grįžimo adresą iš kamino ir įrašant jį į antrąjį vietinį kintamąjį. Pasibaigus pagaliau
sąlyga, ret instrukcija grąžinimo adresą paima iš tinkamos vietos, antrasis vietinis kintamasis.
„HopAround“: „Java“ virtualios mašinos modeliavimas
Žemiau esanti programėlė demonstruoja „Java“ virtualią mašiną, vykdančią baitų kodų seką. Modeliavimo baitų kodų seką sukūrė javac
kompiliatorius apynių aplink ()
toliau nurodytos klasės metodas:
klasė Klounas {static int hopAround () {int i = 0; o (tiesa) {pabandykite {pabandykite {i = 1; } pagaliau {// pirmasis galutinis sakinys i = 2; } i = 3; grįžti i; // tai niekada nebaigiama, nes tęsinys} galiausiai {// antrasis galutinis sakinys if (i == 3) {tęsti; // šis tęsinys pakeičia grąžinimo teiginį}}}}}
Sukurtus baitų kodus javac
už apynys (aplink)
metodas parodytas žemiau: