Förstå minnet i JVM

Som du redan vet kör JVM Java-program inom sig själv. Som vilken virtuell maskin som helst har den sitt eget minnesorganisationssystem.

Det interna minnets layout indikerar hur din Java-applikation fungerar. På så sätt kan flaskhalsar i driften av applikationer och algoritmer identifieras. Låt oss se hur det fungerar.

Förstå minnet i JVM

Viktig! Den ursprungliga Java-modellen var inte tillräckligt bra, så den reviderades i Java 1.5. Denna version används till denna dag (Java 14+).

Trådstapel

Java-minnesmodellen som används internt av JVM delar upp minnet i trådstaplar och högar. Låt oss titta på Java-minnesmodellen, logiskt uppdelad i block:

Trådstapel

Alla trådar som körs i JVM har sin egen stack . Stacken innehåller i sin tur information om vilka metoder tråden har anropat. Jag kommer att kalla detta "samtalsstacken". Anropsstacken återupptas så snart tråden kör sin kod.

Trådens stack innehåller alla lokala variabler som krävs för att exekvera metoder på trådens stack. En tråd kan bara komma åt sin egen stack. Lokala variabler är inte synliga för andra trådar, bara för tråden som skapade dem. I en situation där två trådar kör samma kod skapar de båda sina egna lokala variabler. Således har varje tråd sin egen version av varje lokal variabel.

Alla lokala variabler av primitiva typer ( boolean , byte , short , char , int , long , float , double ) lagras helt på trådstacken och är inte synliga för andra trådar. En tråd kan skicka en kopia av en primitiv variabel till en annan tråd, men kan inte dela en primitiv lokal variabel.

Högen

Högen innehåller alla objekt som skapats i din applikation, oavsett vilken tråd som skapade objektet. Detta inkluderar omslag av primitiva typer (till exempel Byte , Integer , Long , och så vidare). Det spelar ingen roll om objektet skapades och tilldelades en lokal variabel eller skapades som en medlemsvariabel för ett annat objekt, det lagras på högen.

Nedan är ett diagram som illustrerar anropsstacken och lokala variabler (de lagras på stackar) samt objekt (de lagras på högen):

Högen

I fallet där den lokala variabeln är av en primitiv typ, lagras den på trådens stack.

En lokal variabel kan också vara en referens till ett objekt. I det här fallet lagras referensen (lokal variabel) på trådstacken, men själva objektet lagras på högen.

Ett objekt innehåller metoder, dessa metoder innehåller lokala variabler. Dessa lokala variabler lagras också på trådstacken, även om objektet som äger metoden lagras på heapen.

Ett objekts medlemsvariabler lagras på högen tillsammans med själva objektet. Detta gäller både när medlemsvariabeln är av en primitiv typ och när den är en objektreferens.

Statiska klassvariabler lagras också på högen tillsammans med klassdefinitionen.

Interaktion med föremål

Objekt på högen kan nås av alla trådar som har en referens till objektet. Om en tråd har tillgång till ett objekt kan den komma åt objektets variabler. Om två trådar anropar en metod på samma objekt samtidigt, kommer de båda att ha tillgång till objektets medlemsvariabler, men varje tråd kommer att ha sin egen kopia av de lokala variablerna.

Interaktion med objekt (hög)

Två trådar har en uppsättning lokala variabler.Lokal variabel 2pekar på ett delat objekt på högen (Objekt 3). Var och en av trådarna har sin egen kopia av den lokala variabeln med sin egen referens. Deras referenser är lokala variabler och lagras därför på trådstaplar. Två olika referenser pekar dock på samma objekt på högen.

Observera att den allmännaObjekt 3har länkar tillObjekt 2OchObjekt 4som medlemsvariabler (visas med pilar). Genom dessa länkar kan två trådar komma åtObjekt 2OchObjekt4.

Diagrammet visar också en lokal variabel (lokal variabel 1från metod två ). Varje kopia av den innehåller olika referenser som pekar på två olika objekt (Objekt 1OchObjekt 5) och inte samma. Teoretiskt sett kan båda trådarna komma åt bådaObjekt 1, så attObjekt 5om de har referenser till båda dessa objekt. Men i diagrammet ovan har varje tråd bara en referens till ett av de två objekten.

Ett exempel på interaktion med objekt

Låt oss se hur vi kan demonstrera arbetet i kod:

public class MySomeRunnable implements Runnable() {

    public void run() {
        one();
    }

    public void one() {
        int localOne = 1;

        Shared localTwo = Shared.instance;

        //... do something with local variables

        two();
    }

    public void two() {
        Integer localOne = 2;

        //... do something with local variables
    }
}
public class Shared {

    // store an instance of our object in a variable

    public static final Shared instance = new Shared();

    // member variables pointing to two objects on the heap

    public Integer object2 = new Integer(22);
    public Integer object4 = new Integer(44);
}

Metoden run() anropar metoden one() och one() anropar i sin tur two() .

Metoden one() deklarerar en primitiv lokal variabel (localOne) av typen int och en lokal variabel (localTwo), som är en referens till ett objekt.

Varje tråd som kör metoden one() kommer att skapa sin egen kopialocalOneOchlocalTwoi din stack. VariablerlocalOnekommer att vara helt separerade från varandra, vara på traven av varje tråd. En tråd kan inte se vilka ändringar en annan tråd gör i sin kopialocalOne.

Varje tråd som kör metoden one() skapar också sin egen kopialocalTwo. Dock två olika exemplarlocalTwosluta peka på samma föremål på högen. Faktum är attlocalTwopekar på objektet som den statiska variabeln refererar tillexempel. Det finns bara en kopia av en statisk variabel, och den kopian lagras på högen.

Så båda exemplarenlocalTwoslutar peka på samma delade instans . Den delade instansen lagras också på högen. Det matcharObjekt 3i diagrammet ovan.

Observera att klassen Shared också innehåller två medlemsvariabler. Själva medlemsvariablerna lagras på högen tillsammans med objektet. Två medlemsvariabler pekar på två andra objektHeltal. Dessa heltalsobjekt motsvararObjekt 2OchObjekt 4på diagrammet.

Observera också att metoden two() skapar en lokal variabel med namnetlocalOne. Denna lokala variabel är en referens till ett objekt av typen Heltal . Metoden sätter länkenlocalOneför att peka på en ny Integer- instans . Länken kommer att lagras i sin kopialocalOneför varje tråd. Två Integer- instanser kommer att lagras på heapen, och eftersom metoden skapar ett nytt Integer- objekt varje gång det körs, kommer de två trådarna som kör denna metod att skapa separata Integer- instanser . De matcharObjekt 1OchObjekt 5i diagrammet ovan.

Lägg även märke till de två medlemsvariablerna i klassen Shared av typen Integer , som är en primitiv typ. Eftersom dessa variabler är medlemsvariabler, lagras de fortfarande på högen tillsammans med objektet. Endast lokala variabler lagras på trådstacken.