JVM (Mașină Virtuală Java). Esența mașinii virtuale Java

JVM-ul este o componentă cheie a platformei Java. Java Virtual Machine interpretează și execută bytecode Java pregenerat din textul sursă al unui program Java de către compilatorul Java (javac).

Deoarece mașinile virtuale Java sunt disponibile pentru multe platforme hardware și software, Java poate fi considerat atât ca middleware, cât și ca o platformă în sine, de unde principiul „scrie o dată, rulează oriunde”. Utilizarea unui singur bytecode pe mai multe platforme permite ca Java să fie descris ca „compilați o dată, rulați oriunde”.

Programele destinate să ruleze pe JVM trebuie să fie compilate într-un format binar portabil standardizat, care este de obicei reprezentat ca fișiere .class. Un program poate consta din mai multe clase situate în fișiere diferite. Pentru a facilita găzduirea de programe mari, unele fișiere .class pot fi împachetate într-un așa-numit fișier .jar (prescurtare de la Java Archive).

Mașina virtuală JVM execută fișiere .clasă sau .borcan, emulând instrucțiunile scrise pentru JVM prin interpretarea sau utilizarea unui compilator just-in-time (JIT), cum ar fi HotSpot de la microsistemele Sun. În zilele noastre, compilarea JIT este utilizată în majoritatea JVM-urilor pentru a obține o viteză mai mare. Există, de asemenea, compilatoare anticipate care permit dezvoltatorilor de aplicații să precompileze fișierele de clasă în cod nativ pentru o anumită platformă.

JVM, care este o instanță a JRE (Java Runtime Environment), intră în joc atunci când sunt executate programe Java. După finalizarea execuției, această instanță este ștearsă de colectorul de gunoi. JIT este o parte a mașinii virtuale Java care este utilizată pentru a accelera timpul de execuție a aplicațiilor. JIT compilează simultan părți ale bytecode care au funcționalități similare și, prin urmare, reduce timpul necesar pentru compilare.

JVM-ul este o mașină de stivă. Aceasta înseamnă că nu există registre de uz general, iar operațiunile sunt efectuate pe datele aflate pe stivă. Acest scop este servit de un teanc de operanzi alocați în cadrul fiecărui cadru. Când sunt executate comenzi Java bytecode care modifică datele, operanzii acelor comenzi sunt scoși din stiva de operanzi, iar rezultatele comenzilor sunt împinse în aceeași stivă.

Timpul de execuție al unei metode conține informațiile necesare pentru legarea dinamică, returnarea metodei și gestionarea excepțiilor. Codul clasei (găzduit în domeniul de aplicare al clasei) accesează metode și variabile externe folosind legături simbolice. Legătura dinamică traduce legăturile simbolice în legături reale. Mediul de execuție conține referințe la tabelul simbol al metodei, prin care se fac apeluri la metode și variabile externe.

Runtime-ul conține, de asemenea, informațiile necesare pentru a reveni dintr-o metodă: un pointer către cadrul metodei apelante, valoarea registrului computerului de returnat, conținutul registrelor metodei apelante și un pointer către zona pentru a scrie valoarea returnată.

Informațiile de tratare a excepțiilor conțin referințe la secțiunile de tratare a excepțiilor ale unei metode de clasă.

Runtime face, de asemenea, referințe la datele conținute în zona de clasă, inclusiv constante și variabile de clasă.

Instrucțiunile JVM constau dintr-un cod operațional de un bit și pot conține, de asemenea, operanzi. Numărul și dimensiunea operanzilor sunt determinate de opcode unele instrucțiuni nu au operanzi

Multe răspunsuri bune și corecte au fost deja date aici, dar aș dori să clarific că această metaforă este:

Mașina virtuală Java este, de asemenea, în esență un interpret

te poate conduce pe o cale foarte greșită!

Cuvintele din nume au un sens destul de precis, iar JVM-ul este numit exact Cu mașina, și nu un interpret, și nu un compilator, nu este deloc întâmplător. Există un compilator în Java (javac) și este necesar nu pentru executarea programului, ci în mod special pentru compilarea acestuia (în bytecode). De aceea nu este inclus în JRE (mediul de rulare), ci este conținut în JDK (mediul de dezvoltare). JVM-ul în sine are altul, un compilator JIT, care compilează bytecode în instrucțiunile procesorului în timpul execuției programului, dar aceasta este o altă poveste și, de asemenea, nu poate fi numit interpret.

În esență, JVM-ul este un procesor, doar unul virtual. Și ca orice procesor (hardware, tip x86 sau virtual, tip CLR în .NET), are propriul set de opțiuni. coduri numit octet cod. Așa cum x86 poate rula cod generat de un compilator cu C++, sau Pascal sau Go, JVM poate rula bytecode compilat din Java, sau Scala sau Kotlin (sau chiar scris de mână), iar .class -file este în esență același .exe (mai precis .so), compilat pentru „procesorul JVM”. Acesta este ceea ce înseamnă cross-platform. Așa cum codul compilat pentru x86 va rula pe un procesor de la Intel sau AMD, la fel se va rula bytecode JVM pe JVM-uri de la Oracle, IBM, OpenJDK etc. Și chiar și prezența JIT, compilarea bytecode în codul operațional al unui anumit procesor hardware în timpul execuției încă nu oferă motive pentru a apela un stivă onest (SUN) sau a înregistra (Dalvik) VM un interpret, chiar dacă doar în esență :)

Cert este că această clasificare în sine (LIMBAJ interpretat/compilat) a fost practic lipsită de sens în ultimii 25 de ani și ceva. Limbi care au fost inițial orientate spre implementare sub forma unui interpret (cu vocabular simplu analizat, astfel încât interpretul să fie mai mic și să poată lăsa suficient spațiu pentru programul în sine într-o memorie limitată) precum APL sau BASIC, acum (cu excepția , bineînțeles, pentru aplicații foarte specializate) au loc de cinste doar acela din manualele vechi, dintre care tocmai această clasificare, cu tenacitate demnă de mai bine folosită, continuă să fie copiată cuvânt cu cuvânt în cele noi. În același timp, din anumite motive, ei uită să clarifice că aceste două concepte nu mai sunt despre limbile în sine, ci doar despre unele metode de implementare a acestora și că de atunci, pe lângă aceste metode, multe alte bune și au apărut diferite concepte pe această temă (cum ar fi VM, JIT, colectori de gunoi și cel puțin același OOP, diferite tipuri de tastare și un milion de alte lucruri), care pur și simplu nu erau în acele manuale din cauza anului de publicare. Și că astăzi, chiar și pentru limbaje care sunt concepute fundamental pentru compilare pentru o arhitectură de registru, cum ar fi C, există o duzină de interpreți (unul, doi, trei)... pe care, din nou, nimeni nu le numește mașini virtuale , deoarece Toate acestea sunt concepte diferite. Pe scurt, este ca și cum ai încerca să înțelegi unde sunt focul, apa, pământul și aerul în mecanica cuantică, așa cum le-au înțeles Platon și Aristotel :)

P.S. Pentru a înțelege când această clasificare era încă relevantă, recomand acest. Acolo, creatorii APL, unul dintre primele limbi interpretate adevărate, discută problemele stringente ale dezvoltării limbajului din epocă. Dacă engleza este o problemă, măcar uită-te la introducere... acele piese hardware aveau mai puțină memorie și putere de procesare decât o cartelă SIM modernă :)

JVM (Java Virtual Machine) este baza limbajului de programare Java. Mediul Java este format din cinci elemente:
■ Limbajul Java
■Definirea bytecode
■ Biblioteci de clase Java/Sun
■ Mașină virtuală Java
■ Structura fișierului .class

Dintre toate aceste cinci elemente, elementele care au dus la succesul Java
■ Definiția bytecode,
■ file structure.class,
■ și Java Virtual Machine.

Astfel, „scrieți o dată și rulați oriunde” a fost de fapt posibil prin portabilitatea fișierului .class, care facilitează execuția pe orice computer sau chipset folosind Java Virtual Machine.

1.3.1 Ce este mașina virtuală Java?

O mașină virtuală este un software bazat pe conceptele și ideea unui computer imaginar care are un set logic de instrucțiuni și comenzi care definesc operațiunile acelui computer. Este, s-ar putea spune, un sistem de operare mic. Formează nivelul necesar de abstractizare, unde se realizează independența față de platforma și echipamentul utilizat.

Compilatorul convertește textul sursă în cod care se bazează pe setul de instrucțiuni imaginare al computerului și este independent de procesorul specific. Un interpret este o aplicație care înțelege aceste fluxuri de comenzi și traduce aceste comenzi pentru hardware-ul utilizat, căruia îi aparține interpretul. JVM creează un sistem de rulare intern care ajută codul să se execute când
■ încărcarea fișierelor .class,
■ managementul memoriei
■ efectuarea tratării excepţiilor.

Datorită inconsecvenței platformelor hardware, mașina virtuală folosește conceptul de stivă, care conține următoarele informații:
■ Descriptori de stare a metodei
■ Operanzi la coduri de octet
■Parametrii metodei
■ Variabile locale

Când codul este executat folosind JVM, există un registru special care este folosit ca numărător, indicând comenzile care se execută în prezent. Dacă este necesar, comenzile modifică programul, modificând fluxul de execuție, în caz contrar fluxul este secvențial și trece de la o comandă la alta.

Un alt concept care devine popular este utilizarea unui compilator Just In Time (JIT). Browsere precum Netscape Navigator 4.0 și Internet Explorer 4.0 includ compilatoare JIT care măresc viteza de execuție a codului Java. Scopul principal al JIT este de a converti setul de instrucțiuni bytecode în instrucțiuni de cod de mașină care vizează un anumit microprocesor. Aceste comenzi sunt stocate și utilizate ori de câte ori se face o solicitare către acea metodă specifică.

1.3.2 Java runtime

JRE (Java Runtime Environment) JVM interacționează cu hardware-ul pe de o parte și cu programul pe de altă parte. JRE rulează cod compilat pentru JVM:
Se încarcă fișiere .class
S-a terminat folosind „Class Loader”
Încărcătorul de clasă efectuează o verificare de securitate dacă fișierele sunt utilizate în rețea.
Verificare bytecode
Efectuat de un „verificator de bytecode”
Un verificator de bytecode verifică formatul codului, conversiile tipului de obiect și verifică dacă există încălcări ale accesului.
Cod de executare
Executat „de către interpret în timpul execuției”
Interpretul execută bytecode-urile și face cereri pentru hardware-ul utilizat.


Figura 1.3: Java Runtime Environment

1.3.3 Gestionarea excepțiilor și gestionarea memoriei

În C, C++ sau Pascal, programatorii au folosit metode primitive pentru alocarea și eliberarea blocurilor de memorie - memoria dinamică. Memoria dinamică este o bucată mare de memorie, care este desemnată în volumul întregii memorie.

Se folosește memoria dinamică:
Blocklist gratuit
Listă de blocuri distribuite

Lista liberă verifică un bloc de memorie ori de câte ori se face o solicitare. Mecanismul de alocare utilizat este metoda „first fit”, prin care primul cel mai mic bloc de memorie este alocat în funcție de cerere. Această procedură alocă și eliberează cantități mici de memorie de diferite dimensiuni din heap, minimizând în același timp fragmentarea heap.

Există o etapă în care se face o cerere de memorie pentru a obține un bloc de memorie mai mare decât este disponibil. În astfel de cazuri, managerul heap trebuie să creeze mai multă memorie. Această tehnică se numește compactare. Acesta este procesul prin care toate blocurile de memorie disponibile libere sunt combinate împreună prin mutarea memoriei libere la un capăt al memoriei dinamice, creând astfel un bloc mare de memorie.

Mașina virtuală Java utilizează două heap-uri separate pentru alocarea memoriei statice și dinamice.

Memoria dinamică - Nu gestionează excepțiile memoriei dinamice, care păstrează toate proprietățile clasei, pool-ul persistent și tabelele de metode.

A doua memorie dinamică este din nou împărțită în două secțiuni, care pot fi extinse în direcții opuse după cum este necesar. O partiție este folosită pentru a stoca instanțe ale obiectelor, iar cealaltă partiție este folosită pentru a stoca handle-uri pentru acele instanțe. Un descriptor este o structură care constă din doi pointeri. Indicați spre un tabel cu metode obiect și alte elemente către un exemplu al acelui obiect. Acest aranjament elimină în esență nevoia de a menține căi care indică către un obiect atunci când se modifică pointerii după compactare. Tot ce trebuie să facem este să actualizăm valoarea indicatorului de mâner.

Algoritmul de tratare a excepțiilor este aplicat obiectelor plasate în memoria dinamică. Pe măsură ce se primește o solicitare pentru un bloc de memorie, managerul heap verifică mai întâi lista liberă și dacă managerul heap nu poate găsi blocuri de memorie libere, gestionarea excepțiilor este invocată de îndată ce sistemul a fost inactiv pentru o perioadă suficientă de timp. În cazurile în care aplicațiile sunt extrem de interactive și timpul de nefuncționare a sistemului este menținut la minimum, gestionarea excepțiilor ar trebui să fie apelată în mod explicit de către aplicație.

Colectorul de excepții apelează metoda de terminare înainte ca un obiect eșantion să fie colectat utilizând gestionarea excepțiilor. Metoda de închidere este utilizată pentru a curăța resursele externe, cum ar fi fișierele și fluxurile care sunt deschise și nu sunt luate în considerare în gestionarea excepțiilor standard. Chiar dacă numim în mod explicit gestionarea excepțiilor pe metoda (System.gc()), aceasta nu va funcționa rapid. Este doar menit să funcționeze. Aceasta înseamnă, de asemenea, că gestionarea excepțiilor nu poate fi apelată. Acest lucru se datorează faptului că firele de execuție de gestionare a excepțiilor rulează cu prioritate foarte scăzută și pot fi întrerupte frecvent. Acest lucru se poate întâmpla atunci când obiectul nostru nu a fost niciodată localizat în memorie înainte.

Ce este JVM?

JVM este un motor care oferă un mediu de rulare pentru a conduce codul sau aplicațiile Java. Acesta convertește bytecode Java în limbaj de mașini. JVM face parte din JRE (Java Run Environment). Aceasta înseamnă Java Virtual Machine

  • În alte limbaje de programare, compilatorul produce cod de mașină pentru un anumit sistem. Cu toate acestea, compilatorul Java produce cod pentru o mașină virtuală cunoscută sub numele de mașină virtuală Java.
  • În primul rând, codul Java este compilat în bytecode. Acest bytecode este interpretat pe diferite mașini
  • Între sistemul gazdă și sursa Java, Bytecode este un limbaj intermediar.
  • JVM este responsabil pentru alocarea spațiului de memorie.

În acest tutorial, veți învăța-

Arhitectura JVM

Să înțelegem Arhitectura JVM Conține classloader, zonă de memorie, motor de execuție etc.

1) ClassLoader

Încărcătorul de clasă este un subsistem folosit pentru încărcarea fișierelor de clasă. Îndeplinește trei funcții majore și anume. Încărcare, conectare și inițializare.

2) Zona Metodă

Zona de metode JVM stochează structuri de clasă, cum ar fi metadate, pool-ul constant de rulare și codul pentru metode.

Toate obiectele, variabilele lor de instanță aferente și matricele sunt stocate în heap. Această memorie este comună și partajată în mai multe fire.

4) Stive de limbaj JVM

Stivele de limbaj Java stochează variabile locale și sunt rezultate parțiale. Fiecare thread are propria sa stivă JVM, creată simultan pe măsură ce este creat firul. Un nou cadru este creat ori de câte ori este invocată o metodă și este șters când procesul de invocare a metodei este finalizat.

5) Registre PC

Registrul PC stochează adresa instrucțiunii mașinii virtuale Java care se execută în prezent. În Java, fiecare fir are un registru PC separat.

6) Stive de metode native

Stivele de metode native dețin instrucțiunile codului nativ depinde de biblioteca nativă. Este scris într-o altă limbă în loc de Java.

7) Motor de execuție

Este un tip de software folosit pentru a testa hardware, software sau sisteme complete. Motorul de execuție a testului nu conține niciodată informații despre produsul testat.

8) Interfață Native Method

Interfața Native Method este un cadru de programare. Permite apelarea codului Java care rulează într-un JVM de către biblioteci și aplicații native.

9) Biblioteci cu metode native

Native Libraries este o colecție de Native Libraries (C, C++) care sunt necesare pentru Execution Engine.

Procesul de compilare și execuție a codului software

Pentru a scrie și a executa un program software, aveți nevoie de următoarele

1) Editor– Pentru a introduce programul, ar putea fi folosit un bloc de note

2) Compilator– Pentru a converti programul în limbaj înalt în cod nativ de mașină

3) Linker– Pentru a combina diferite fișiere de referință de program în programul principal.

4) Încărcător– Pentru a încărca fișierele de pe dispozitivul de stocare secundar, cum ar fi hard disk, flash drive, CD în RAM pentru execuție. Încărcarea se face automat când executați codul.

5) Executarea– Execuția efectivă a codului care este gestionată de sistemul de operare și procesorul dumneavoastră.

Cu acest fundal, consultați următorul videoclip și aflați funcționarea și arhitectura mașinii virtuale Java.

Procesul de compilare și execuție a codului C

Pentru a înțelege procesul de compilare Java în Java. Să aruncăm mai întâi o privire rapidă asupra procesului de compilare și conectare în C.

Să presupunem că, în principal, ați apelat două funcții f1 și f2. Funcția principală este stocată în fișierul a1.c.

Funcția f1 este stocată într-un fișier a2.c

Funcția f2 este stocată într-un fișier a3.c

Toate aceste fișiere, adică a1.c, a2.c și a3.c, sunt transmise compilatorului. A cărui ieșire sunt fișierele obiect corespunzătoare care sunt codul mașinii.

Următorul pas este integrarea tuturor acestor fișiere obiect într-un singur fișier .exe cu ajutorul linkerului. Linker-ul va combina toate aceste fișiere și va produce fișierul .exe.

În timpul rulării programului, un program de încărcare va încărca a.exe în RAM pentru execuție.

Compilarea și execuția codului Java în VM Java

Să ne uităm la procesul pentru JAVA. În principal, aveți două metode f1 și f2.

  • Metoda principală este stocată în fișierul a1.java
  • f1 este stocat într-un fișier ca a2.java
  • f2 este stocat într-un fișier ca a3.java

Compilatorul va compila cele trei fișiere și va produce 3 fișiere .class corespunzătoare care constă din codul BYTE. Spre deosebire de C, nu se face nicio legătură.

Java VM sau Java Virtual Machine se află pe RAM. În timpul execuției, folosind încărcătorul de clasă, fișierele de clasă sunt aduse pe RAM. Codul BYTE este verificat pentru orice încălcare a securității.

Apoi, motorul de execuție va converti Bytecode în cod nativ de mașină. Aceasta este compilarea la timp. Este unul dintre motivele principale pentru care Java este relativ lent.

NOTĂ: JIT sau Compilatorul Just-in-time face parte din Java Virtual Machine (JVM). Acesta interpretează o parte din codul octet care are o funcționalitate similară în același timp.

De ce este Java atât limbajul interpretat, cât și limbajul compilat?

Limbajele de programare sunt clasificate ca
  • Limbaj de nivel superior Ex. C++, Java
  • Limbi de nivel mediu Ex. C
  • Limbaj de nivel scăzut Ex Assembly
  • în cele din urmă cel mai de jos nivel ca Limbajul Mașină.

A compilator este un program care convertește un program de la un nivel de limbaj la altul. Exemplu de conversie a programului C++ în cod mașină.

Compilatorul java convertește codul java de nivel înalt în bytecode (care este, de asemenea, un tip de cod de mașină).

Un interpret este un program care convertește un program la un nivel într-un alt limbaj de programare la acelasi nivel. Exemplu de conversie a programului Java în C++

În Java, generatorul Just In Time Code convertește bytecode în codul mașină nativ care se află la aceleași niveluri de programare.

Prin urmare, Java este atât limbaj compilat, cât și interpretat.

De ce Java este lent?

Cele două motive principale din spatele încetinirii Java sunt

  1. Legătura dinamică: Spre deosebire de C, legarea se face în timpul rulării, de fiecare dată când programul este rulat în Java.
  2. Interpret de rulare: Conversia codului de octeți în cod de mașină nativ se face în timpul rulării în Java, ceea ce încetinește viteza

Cu toate acestea, cea mai recentă versiune de Java a abordat blocajele de performanță într-o mare măsură.

rezumat:

  • JVM sau Java Virtual Machine este motorul care conduce codul Java. Acesta convertește bytecode Java în limbaj de mașini.
  • În JVM, codul Java este compilat în bytecode. Acest bytecode este interpretat pe diferite mașini
  • JIT sau compilatorul Just-in-time face parte din Java Virtual Machine (JVM). Este folosit pentru a accelera timpul de execuție
  • În comparație cu alte mașini de compilare, Java poate fi lentă în execuție.

Eroarea „Nu s-a putut crea mașina virtuală Java” apare în toate versiunile sistemului de operare Windows. Apare atât la lansarea jocurilor care necesită o mașină virtuală Java pe dispozitiv, cât și la instalarea mașinii virtuale în sine pe computer. Textul de eroare Java Virtual Machine Launcher ne spune următoarele: Sistemul nu a putut crea Java Virtual Machine. În acest articol, ne vom uita la motivele pentru care apare această problemă și, desigur, vom elimina eroarea în sine.

Fereastra de eroare „Nu s-a putut crea mașina virtuală Java”

Cel mai adesea, jucătorii Minecraft se plâng de această eroare. Când fac clic pe lansator și lansează din nou jocul lor preferat, utilizatorii se confruntă cu fereastra de eroare Nu a putut crea mașina virtuală Java. Acest lucru se întâmplă deoarece sesiunea de joc a fost încheiată incorect data anterioară. Poate că nu ați așteptat până când jocul s-a terminat complet și ați oprit dispozitivul.

Erorile JVM la lansarea jocurilor și aplicațiilor pot apărea și din cauza RAM insuficientă pe dispozitiv. Mașina virtuală Java necesită o anumită cantitate de memorie alocată de computer pentru a funcționa. Aceasta este o resursă foarte valoroasă pentru un computer, cu cât este mai multă memorie, cu atât mai rapid și mai bine face față sarcinilor atribuite.

Depanarea erorii Java Virtual Machine Launcher

Să ne uităm la cea mai comună modalitate de a rezolva eroarea „Nu s-a putut crea mașina virtuală Java” - crearea unei noi variabile de mediu.


Metoda 2. Eliberați memoria RAM pentru computer

Următoarea metodă de a rezolva eroarea este eliberarea memoriei RAM a computerului. După cum sa menționat deja, eroarea poate apărea din cauza memoriei insuficiente. Pentru a-l elibera, trebuie să închideți toate programele inutile și, de asemenea, să „omorâți” toate procesele inutile. La urma urmei, fiecare program are nevoie de o anumită cantitate din această resursă. Pe site-ul oficial Microsoft vă puteți familiariza cu toate procesele importante Windows, a căror terminare va duce la defecțiuni ale sistemului. Pentru a opri procesele inutile:


Pentru a închide un program sau a opri un proces, trebuie să selectați numele programului sau al procesului cu mouse-ul, apoi faceți clic pe butonul din partea de jos a ferestrei „Încheierea sarcinii”.

Unele programe care rulează în fundal nu apar în listă, dar pot fi văzute în lista de procese. Acestea sunt diverse module de actualizare, funcționează în fundal și monitorizează lansarea de noi versiuni ale anumitor programe. De asemenea, consumă RAM. Sarcina dvs. va fi să găsiți astfel de procese și să le opriți pentru a rezolva problema actuală. După ce ați șters memoria și ați oprit toate programele și procesele inutile, încercați să reporniți jocul pentru a vă asigura că fereastra de eroare „Nu s-a putut crea mașina virtuală Java” nu mai apare.

Metode suplimentare de rezolvare a erorilor

Dacă software-ul care produce o eroare la pornire a fost descărcat din resurse terțe, site-uri warez sau trackere torrent, atunci acțiunile sale sunt adesea blocate de software-ul antivirus. Pentru a evita un astfel de obstacol la pornire, trebuie să verificați lista de carantină antivirus și, dacă există jocuri sau programe în ea, le puteți elimina din această listă. Dar ai grijă când faci asta. La urma urmei, un astfel de software poate reprezenta cu adevărat o amenințare pentru sistem. Dacă sunteți complet încrezător într-un program sau joc, îl puteți adăuga la lista de excluderi. În acest caz, antivirusul nu va mai „bănui” un astfel de software.

Dacă nu aveți instalat software-ul Java, îl puteți descărca de pe https://www.java.com/ru/download/win8.jsp. Odată ajuns pe pagină, faceți clic pe butonul „Sunt de acord și începe descărcarea gratuită”.


Descărcați pachetul software Java

După aceasta, va fi descărcat un pachet de date, pe care va trebui să îl instalați pe computer.