先上一張JVM體系結構圖:html
1)運行時數據區:通過編譯生成的字節碼文件(class文件),由class loader(類加載子系統)加載後交給執行引擎執行。在執行引擎執行的過程當中產生的數據會存儲在一塊內存區域。這塊內存區域就是運行時區域java
2)程序計數器:用於記錄當前線程的正在執行的字節碼指令位置。因爲虛擬機的多線程是切換線程並分配cpu執行時間的方式實現的,不一樣線程的執行位置都須要記錄下來,所以程序計數器是線程私有的多線程
3)虛擬機棧:虛擬機棧是java方法執行的內存結構,虛擬機會在每一個java方法執行時建立一個「棧楨」,用於存儲局部變量表,操做數棧,動態連接,方法出口等信息。當方法執行完畢時,該棧楨會從虛擬機棧中出棧。其中局部變量表包含基本數據類型和對象引用;函數
在java虛擬機規範中,對這個區域規定了兩種異常狀態:若是線程請求的棧的深度大於虛擬機容許的深度,將拋出StackOverFlowError異常(棧溢出),若是虛擬機棧能夠動態擴展(如今大部分java虛擬機均可以動態擴展,只不過java虛擬機規範中也容許固定長度的java虛擬機棧),若是擴展時沒法申請到足夠的內存空間,就會拋出OutOfmMemoryError異常(沒有足夠的內存)post
4)本地方法棧:相似java方法的執行有虛擬機棧,本地方法的執行則對應有本地方法棧優化
5)方法區:用於存儲已被虛擬機加載的類信息,常量,靜態變量,即時編譯器編譯後的代碼等數據。線程共享(看存儲的數據就知道了)ui
java虛擬機規範對方法區的限制很是寬鬆,除了和java堆同樣不須要連續的內存和能夠選擇固定大小或者可擴展外,還能夠選擇不實現垃圾收集。相對而言,垃圾收集在這個區域是比較少出現的,但並不是數據進入了方法區就如永久代的名字同樣永久存在了。這區域的內存回收目標重要是針對常量池的回收和類型的卸載,通常來講這個內存區域的回收‘成績’比較難以使人滿意。尤爲是類型的卸載條件很是苛刻,可是這部分的回收確實是必要的。在sun公司的bug列表中,曾出現過的若干個嚴重的bug就是因爲低版本的HotSpot虛擬機對此區域未完成回收致使的內存溢出。spa
6)java堆(java Heap):堆的主要做用是存放程序運行過程當中建立的對象實例,由於要存放的對象實例有可能會極多,所以也是虛擬機內存管理中最大的一塊。而且因爲硬件條件有限,因此須要不斷回收已「無用」的實例對象來騰出空間給新生成的實例對象;所以java的垃圾回收主要是針對堆進行回收的(還有方法區的常量池),java堆不少時候也被稱爲GC堆(Garbage Collected Heap)。線程
7)類加載機制(Class Loader):類加載子系統是根據一個類的全限定名來加載該類的二進制流到內存中,在JVM中將造成一份描述Class結構的元信息對象(方法區),經過該元信息對象能夠獲知Class的結構信息:如構造函數,屬性和方法等,Java容許用戶藉由這個Class相關的元信息對象間接調用Class對象的功能。htm
好!說了這麼多關鍵字,再拿例子來說解一下這些關鍵字:
A.圖1是咱們寫的HelloWorld.java,經過IDE或命令:javac HelloWorld 編譯生成16進制的HelloWorld.class(字節碼文件,見圖3),想讀懂16進制字節可參考:一文讓你明白java字節碼 ;但通常IDE會自動轉譯成圖2的指令;或者經過命令:javap -verbose HelloWorld 進行轉譯。
(圖1)HelloWorld.java
(圖2)HelloWorld.class
(圖3)16進制的字節碼:
B.接着,當咱們經過IDE或者命令:java HelloWorld 運行這個class文件時,字節碼文件(class文件)經過類加載機制加載完畢交付給執行引擎執行;類加載機制把HelloWrold類的信息、靜態變量(例子中沒加)、常量(例子中沒加,常量會加載到方法區的常量池,這和靜態變量不同)等加載到方法區中,接下來若是須要建立該類的對象,須要經過new後面帶的參數到方法區進行查找類相關信息。
C.類加載完後,虛擬機會檢查程序的入口,虛擬機中程序的執行入口爲main函數,如HelloWorld.class中,,執行引擎找到main函數開始執行指令,並生成一個「楨棧」入棧至虛擬機棧的棧頂;咱們能夠看到(圖2)在main方法下面的命令:0 new java.lang.StringBuilder [16] 表示建立一個String對象,建立的String對象實例會在java堆(Heap)中分配內存存儲(Java對象在JVM中的建立過程能夠看這篇文章:Java對象是怎麼建立的(經過對象的建立,瞭解JVM內存結構)),並把該指令位置「0」記錄到當前線程的程序計數器中;3 dup 而後把該對象的引用壓入虛擬機棧中,並把該指令位置「3」記錄到當前線程的程序計數器中;4 ldc <String "Hello"> [18] 從字符串常量池(從jdk1.7開始,字符串常量池被移動到java堆)加載字符串常量Hello,並更新指令位置到程序計數器;...若是執行過程當中有本地方法的指令,則會在本地方法棧中進行出入棧;這裏有個點注意一下,請看main函數指令16的位置: 16 new java.lang.StringBuilder [31] 這裏建立了一個StringBuilder對象,自jdk5開始已對這種類型的字符串拼接進行了優化,具體自行谷歌補充。
D.執行引擎執行指令過程當中,按需調用本地庫接口以執行本地庫方法,如new指令、輸出屏幕等操做
以上就是一個HelloWorld執行過程在JVM中發生的事情。
參考:
《深刻理解Java虛擬機:JVM高級特性與最佳實踐》一書
https://www.cnblogs.com/IUbanana/p/7067362.html