A memória értelmezése a JVM-ben

Mint már tudja, a JVM Java programokat futtat magában. Mint minden virtuális gépnek, ennek is megvan a maga memóriaszervezési rendszere.

A belső memória elrendezése jelzi, hogyan működik a Java-alkalmazás. Így azonosíthatók az alkalmazások és az algoritmusok működésének szűk keresztmetszete. Lássuk, hogyan működik.

A memória értelmezése a JVM-ben

Fontos! Az eredeti Java modell nem volt elég jó, ezért átdolgozták a Java 1.5-ben. Ezt a verziót a mai napig használják (Java 14+).

Thread Stack

A JVM által belsőleg használt Java memóriamodell szálveremekre és kupacokra osztja a memóriát. Nézzük meg a Java memóriamodellt, logikailag blokkokra bontva:

Thread Stack

A JVM-ben futó összes szálnak saját veremük van . A verem viszont információkat tartalmaz arról, hogy a szál mely metódusokat hívta meg. Ezt „hívás veremnek” fogom hívni. A hívási verem folytatódik, amint a szál végrehajtja a kódját.

A szál verem tartalmazza az összes helyi változót, amely a metódusok végrehajtásához szükséges a szál veremében. Egy szál csak a saját vereméhez férhet hozzá. A helyi változók nem láthatók más szálak számára, csak az azokat létrehozó szál számára. Abban a helyzetben, amikor két szál ugyanazt a kódot hajtja végre, mindkettő létrehozza a saját helyi változóit. Így minden szálnak megvan a saját verziója minden helyi változóról.

Az összes primitív típusú helyi változó ( logikai , bájt , short , char , int , long , float , double ) teljes egészében a szálveremben tárolódik, és nem láthatók más szálak számára. Egy szál átadhatja egy primitív változó másolatát egy másik szálnak, de nem oszthat meg egy primitív helyi változót.

Halom

A kupac tartalmazza az alkalmazásban létrehozott összes objektumot, függetlenül attól, hogy melyik szál hozta létre az objektumot. Ide tartoznak a primitív típusú burkolók (például Byte , Integer , Long és így tovább). Nem számít, hogy az objektumot létrehozták és egy helyi változóhoz rendelték hozzá, vagy egy másik objektum tagváltozójaként hozták létre, a kupacban tárolódik.

Az alábbiakban egy diagramon látható a hívási verem és a helyi változók (a veremekben vannak tárolva), valamint az objektumok (a kupacban vannak tárolva):

Halom

Abban az esetben, ha a helyi változó primitív típusú, akkor a szál veremében tárolódik.

A lokális változó hivatkozás is lehet egy objektumra. Ebben az esetben a hivatkozást (helyi változót) a szál verem tárolja, de magát az objektumot a kupacban tárolja.

Egy objektum metódusokat tartalmaz, ezek a metódusok helyi változókat tartalmaznak. Ezeket a helyi változókat a szál verem is tárolja, még akkor is, ha a metódus tulajdonosa a kupacban van tárolva.

Az objektum tagváltozói a kupacban tárolódnak magával az objektummal együtt. Ez akkor is igaz, ha a tagváltozó primitív típusú, és akkor is, ha objektumhivatkozásról van szó.

A statikus osztályváltozók szintén a kupacban tárolódnak az osztálydefinícióval együtt.

Interakció tárgyakkal

A kupac objektumaihoz minden olyan szál hozzáférhet, amelyik hivatkozik az objektumra. Ha egy szál hozzáfér egy objektumhoz, akkor hozzáférhet az objektum változóihoz. Ha két szál egyszerre hív meg egy metódust ugyanazon az objektumon, akkor mindkettőnek hozzáférése lesz az objektum tagváltozóihoz, de mindegyik szálnak megvan a saját másolata a helyi változókról.

Interakció tárgyakkal (kupac)

Két szál helyi változókkal rendelkezik.2. helyi változóegy megosztott objektumra mutat a kupacban (3. objektum). Mindegyik szálnak megvan a saját másolata a helyi változóról, saját hivatkozással. Hivatkozásaik helyi változók, ezért szál veremekben tárolódnak. Két különböző hivatkozás azonban ugyanarra az objektumra mutat a kupacban.

Felhívjuk figyelmét, hogy az általános3. objektumlinkjei vannak2. objektumÉs4. objektumtagváltozókként (nyilakkal jelölve). Ezeken a linkeken keresztül két szál férhet hozzá2. objektumÉsTárgy4.

A diagram egy helyi változót is mutat (helyi változó 1a Metódusból ) . Minden példánya különböző hivatkozásokat tartalmaz, amelyek két különböző objektumra mutatnak (1. objektumÉs5. objektum) és nem ugyanaz. Elméletileg mindkét szál elérheti mindkettőt1. objektum, szóval5. objektumha mindkét objektumra hivatkoznak. De a fenti diagramon minden szál csak a két objektum egyikére hivatkozik.

Példa a tárgyakkal való interakcióra

Lássuk, hogyan tudjuk kódban bemutatni a munkát:

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);
}

A run() metódus meghívja a one() metódust , a one() pedig a two() -t .

A one() metódus deklarál egy primitív lokális változót (localOne) int típusú és helyi változó (helyiKettő), amely egy objektumra való hivatkozás.

Minden egyes , a one() metódust végrehajtó szál létrehozza a saját másolatátlocalOneÉshelyiKettőa veremben. VáltozóklocalOneteljesen elkülönülnek egymástól, minden szál veremén lesznek. Az egyik szál nem láthatja, hogy egy másik szál milyen változtatásokat hajt végre a másolatánlocalOne.

Minden egyes, a one() metódust végrehajtó szál létrehozza a saját másolatáthelyiKettő. Azonban két különböző példányhelyiKettővégül ugyanarra a tárgyra mutat a kupacban. A tény az, hogyhelyiKettőa statikus változó által hivatkozott objektumra mutatpélda. Egy statikus változónak csak egy példánya van, és ez a példány a kupacban tárolódik.

Tehát mindkét példányhelyiKettővégül ugyanarra a megosztott példányra mutat . A megosztott példány is a kupacban tárolódik. Egyezik3. objektuma fenti diagramon.

Vegye figyelembe, hogy a Shared osztály két tagváltozót is tartalmaz. Maguk a tagváltozók az objektummal együtt a kupacban tárolódnak. Két tagváltozó két másik objektumra mutatEgész szám. Ezek az egész objektumok megfelelnek2. objektumÉs4. objektuma diagramon.

Vegye figyelembe azt is, hogy a two() metódus létrehoz egy nevű helyi változótlocalOne. Ez a helyi változó egy Integer típusú objektumra való hivatkozás . A metódus beállítja a hivatkozástlocalOnehogy egy új Integer példányra mutasson . A hivatkozás a másolatában kerül tárolásralocalOneminden szálhoz. Két Integer példány kerül tárolásra a kupacban, és mivel a metódus minden egyes végrehajtásakor új Integer objektumot hoz létre, a metódust végrehajtó két szál külön Integer példányokat hoz létre . Egyeznek1. objektumÉs5. objektuma fenti diagramon.

Figyeljük meg az Integer típusú Shared osztály két tagváltozóját is , amely primitív típus. Mivel ezek a változók tagváltozók, továbbra is a kupacban tárolódnak az objektummal együtt. Csak a helyi változók tárolódnak a szál veremben.