作者丨西樓有酒
https://www.cnblogs.com/JunFengChan/p/9250585.html


1)運行時(shí)數據區:經(jīng)過(guò)編譯生成的字節碼文件(class文件),由class loader(類(lèi)加載子系統)加載后交給執行引擎執行。在執行引擎執行的過(guò)程中產(chǎn)生的數據會(huì )存儲在一塊內存區域。這塊內存區域就是運行時(shí)區域
2)程序計數器:用于記錄當前線(xiàn)程的正在執行的字節碼指令位置。由于虛擬機的多線(xiàn)程是切換線(xiàn)程并分配cpu執行時(shí)間的方式實(shí)現的,不同線(xiàn)程的執行位置都需要記錄下來(lái),因此程序計數器是線(xiàn)程私有的
3)虛擬機棧:虛擬機棧是java方法執行的內存結構,虛擬機會(huì )在每個(gè)java方法執行時(shí)創(chuàng )建一個(gè)“棧楨”,用于存儲局部變量表,操作數棧,動(dòng)態(tài)鏈接,方法出口等信息。當方法執行完畢時(shí),該棧楨會(huì )從虛擬機棧中出棧。其中局部變量表包含基本數據類(lèi)型和對象引用;
在java虛擬機規范中,對這個(gè)區域規定了兩種異常狀態(tài):如果線(xiàn)程請求的棧的深度大于虛擬機允許的深度,將拋出StackOverFlowError異常(棧溢出),如果虛擬機??梢詣?dòng)態(tài)擴展(現在大部分java虛擬機都可以動(dòng)態(tài)擴展,只不過(guò)java虛擬機規范中也允許固定長(cháng)度的java虛擬機棧),如果擴展時(shí)無(wú)法申請到足夠的內存空間,就會(huì )拋出OutOfmMemoryError異常(沒(méi)有足夠的內存)
4)本地方法棧:類(lèi)似java方法的執行有虛擬機棧,本地方法的執行則對應有本地方法棧
5)方法區:用于存儲已被虛擬機加載的類(lèi)信息,常量,靜態(tài)變量,即時(shí)編譯器編譯后的代碼等數據。線(xiàn)程共享(看存儲的數據就知道了)
java虛擬機規范對方法區的限制非常寬松,除了和java堆一樣不需要連續的內存和可以選擇固定大小或者可擴展外,還可以選擇不實(shí)現垃圾收集。相對而言,垃圾收集在這個(gè)區域是比較少出現的,但并非數據進(jìn)入了方法區就如永久代的名字一樣永久存在了。
這區域的內存回收目標重要是針對常量池的回收和類(lèi)型的卸載,一般來(lái)說(shuō)這個(gè)內存區域的回收'成績(jì)’比較難以令人滿(mǎn)意。尤其是類(lèi)型的卸載條件非??量?,但是這部分的回收確實(shí)是必要的。在sun公司的bug列表中,曾出現過(guò)的若干個(gè)嚴重的bug就是由于低版本的HotSpot虛擬機對此區域未完成回收導致的內存溢出。
6)java堆(java Heap):堆的主要作用是存放程序運行過(guò)程中創(chuàng )建的對象實(shí)例,因為要存放的對象實(shí)例有可能會(huì )極多,因此也是虛擬機內存管理中最大的一塊。并且由于硬件條件有限,所以需要不斷回收已“無(wú)用”的實(shí)例對象來(lái)騰出空間給新生成的實(shí)例對象;因此java的垃圾回收主要是針對堆進(jìn)行回收的(還有方法區的常量池),java堆很多時(shí)候也被稱(chēng)為GC堆(Garbage Collected Heap)。
7)類(lèi)加載機制(Class Loader):類(lèi)加載子系統是根據一個(gè)類(lèi)的全限定名來(lái)加載該類(lèi)的二進(jìn)制流到內存中,在JVM中將形成一份描述Class結構的元信息對象(方法區),通過(guò)該元信息對象可以獲知Class的結構信息:如構造函數,屬性和方法等,Java允許用戶(hù)借由這個(gè)Class相關(guān)的元信息對象間接調用Class對象的功能。

A.圖1是我們寫(xiě)的HelloWorld.java,通過(guò)IDE或命令:javac HelloWorld 編譯生成16進(jìn)制的HelloWorld.class(字節碼文件,見(jiàn)圖3),想讀懂16進(jìn)制字節可參考:http://www.importnew.com/24088.html
但一般IDE會(huì )自動(dòng)轉譯成圖2的指令;或者通過(guò)命令:javap -verbose HelloWorld 進(jìn)行轉譯。
(圖1)HelloWorld.java

(圖2)HelloWorld.class


(圖3)16進(jìn)制的字節碼:

B.接著(zhù),當我們通過(guò)IDE或者命令:java HelloWorld 運行這個(gè)class文件時(shí),字節碼文件(class文件)通過(guò)類(lèi)加載機制加載完畢交付給執行引擎執行;類(lèi)加載機制把HelloWrold類(lèi)的信息、靜態(tài)變量(例子中沒(méi)加)、常量(例子中沒(méi)加,常量會(huì )加載到方法區的常量池,這和靜態(tài)變量不一樣)等加載到方法區中,接下來(lái)如果需要創(chuàng )建該類(lèi)的對象,需要通過(guò)new后面帶的參數到方法區進(jìn)行查找類(lèi)相關(guān)信息。
C.類(lèi)加載完后,虛擬機會(huì )檢查程序的入口,虛擬機中程序的執行入口為main函數,如HelloWorld.class中,,執行引擎找到main函數開(kāi)始執行指令,并生成一個(gè)“楨?!比霔V撂摂M機棧的棧頂;
我們可以看到(圖2)在main方法下面的命令:0 new java.lang.StringBuilder [16] 表示創(chuàng )建一個(gè)String對象,創(chuàng )建的String對象實(shí)例會(huì )在java堆(Heap)中分配內存存儲(Java對象在JVM中的創(chuàng )建過(guò)程可以看這篇文章:
https://www.cnblogs.com/JunFengChan/p/9457685.html
,并把該指令位置“0”記錄到當前線(xiàn)程的程序計數器中;3 dup 然后把該對象的引用壓入虛擬機棧中,并把該指令位置“3”記錄到當前線(xiàn)程的程序計數器中;4 ldc <String "Hello"> [18] 從字符串常量池(從jdk1.7開(kāi)始,字符串常量池被移動(dòng)到j(luò )ava堆)加載字符串常量Hello,并更新指令位置到程序計數器;
...如果執行過(guò)程中有本地方法的指令,則會(huì )在本地方法棧中進(jìn)行出入棧;這里有個(gè)點(diǎn)注意一下,請看main函數指令16的位置: 16 new java.lang.StringBuilder [31] 這里創(chuàng )建了一個(gè)StringBuilder對象,自jdk5開(kāi)始已對這種類(lèi)型的字符串拼接進(jìn)行了優(yōu)化,具體自行谷歌補充。
D.執行引擎執行指令過(guò)程中,按需調用本地庫接口以執行本地庫方法,如new指令、輸出屏幕等操作
以上就是一個(gè)HelloWorld執行過(guò)程在JVM中發(fā)生的事情。
聯(lián)系客服