深入了解JVM內存結構
你對JVM內存結構是否熟悉,這里向大家描述一下,主要包括類裝載子系統,方法區,PC寄存器,堆和棧等內容,其中方法區被所有線程共享,垃圾收集也會清理方法區中的無用類型對象。
JVM內存結構
1)JVM內存結構之類裝載子系統
裝載連接初始化
(2)JVM內存結構之方法區。
被所有線程共享。垃圾收集也會清理方法區中的無用類型對象。
a.類型信息。
類加載器加載類時,從類文件中提取出來。
類的完整有效名
父類的完整有效名(interfaceandjava.lang.Object除外,因為無父類)
類型的修飾符
類型直接接口列表
b.常量池。
存儲了一個類型所使用的常量所有類型、域和方法的符號引用。
c.域信息。
jvm必須在方法區中保存類型的所有域的相關信息以及域的聲明順序,
域的相關信息包括:
域名
域類型
域修飾符(publicprivateprotectedstaticfinalvolatiletransient…)
d.方法信息。
方法名
方法返回類型
方法參數
方法的修飾符
方法的字節碼(abstractandnative除外)(被PC寄存器指向)
操作數棧和方法棧幀的局部變量區的大小#p#
異常表
e.類的靜態變量(所有對象共享一分拷貝)
f.類的被聲明為final的類變量(所有對象共享一分拷貝)
g.加載一個類的類加載器的引用
h.Class類的引用
i.方法表。
j.一個例子:
- ClassLava{
- privateintspeed=5;
- voidflow();
- }
- ClassVolcano{
- publicstaticvoidmain(String[]args){
- Lavalava=newLava();
- lava.flow();
- }
- }
下面我們描述一下main()方法的***條指令的字節碼是如何被執行的。不同的jvm實現的差別很大,這里只是其中之一。
為了運行這個程序,你以某種方式把“Volcano"傳給了jvm。有了這個名字,jvm找到了這個類文件(Volcano.class)并讀入,它從類文件提取了類型信息并放在了方法區中,通過解析存在方法區中的字節碼,jvm激活了main()方法,在執行時,jvm保持了一個指向當前類(Volcano)常量池的指針。
注意jvm在還沒有加載Lava類的時候就已經開始執行了。正像大多數的jvm一樣,不會等所有類都加載了以后才開始執行,它只會在需要的時候才加載。
main()的***條指令告知jvm為列在常量池***項的類分配足夠的內存。
jvm使用指向Volcano常量池的指針找到***項,發現是一個對Lava類的符號引用,然后它就檢查方法區看lava是否已經被加載了。
這個符號引用僅僅是類lava的完整有效名”lava“。這里我們看到為了jvm能盡快從一個名稱找到一個類,一個良好的數據結構是多么重要。這里jvm的實現者可以采用各種方法,如hash表,查找樹等等。同樣的算法可以用于Class類的forName()的實現。
當jvm發現還沒有加載過一個稱為"Lava"的類,它就開始查找并加載類文件"Lava.class"。它從類文件中抽取類型信息并放在了方法區中。
jvm于是以一個直接指向方法區lava類的指針替換了常量池***項的符號引用。以后就可以用這個指針快速的找到lava類了。而這個替換過程稱為常量池解析(constantpoolresolution)。在這里我們替換的是一個native指針。
jvm終于開始為新的lava對象分配空間了。這次,jvm仍然需要方法區中的信息。它使用指向lava數據的指針(剛才指向volcano常量池***項的指針)找到一個lava對象究竟需要多少空間。
一旦jvm知道了一個Lava對象所要的空間,它就在堆上分配這個空間并把這個實例的變量speed初始化為缺省值0。假如lava的父對象也有實例變量,則也會初始化。
當把新生成的lava對象的引用壓到棧中,***條指令也結束了。下面的指令利用這個引用激活java代碼把speed變量設為初始值,5。另外一條指令會用這個引用激活Lava對象的flow()方法。
(3)JVM內存結構之堆。
存放運行時所有對象和數組。
(4)JVM內存結構之棧。
每次啟動一個新的線程,就會被分配一個棧。
(5)JVM內存結構之PC寄存器(程序計數器)
總是指向該線程下一步要執行的指令。指令的位置放在方法區的方法字節碼中。內容是相對于***個指令的偏移量。
(6)JVM內存結構之本地方法棧。
【編輯推薦】