? ? ? ?本篇博文是JVM系列博文的第一篇, 主要講述HotSpot虛擬機在執(zhí)行Java程序的時候運行時數(shù)據(jù)區(qū)域(JVM)的劃分。主要包括以下5個部分,如下圖所示:

1、方法區(qū)
? ? ? ? 方法區(qū)是各個線程共享的區(qū)域,很多時候我們也叫它“永久代”。它用于存儲已被虛擬機加載的類信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)。此外,運行時常量池也是方法區(qū)的一部分,Class文件中除了有類的版本、字段、方法和接口等信息外,還有就是常量了。用于存放編譯期生成的各種字面量和符號引用。比如應用在啟動的時候會將 Integer類型的 1 ~ 128放入常量池中(享元模式),String類型的值以及final修飾的對象等都會放入常量池中。
2、堆
? ? ? ? 堆與方法區(qū)一樣,也是各個線程共享的區(qū)域,也是GC回收的主要場所。Java堆是Java虛擬機所管理的內(nèi)存中最大的一塊,在虛擬機啟動時創(chuàng)建。Java堆得唯一作用就是存放對象實例,幾乎所有的對象實例都在這里分配。從GC回收的角度上講,Java堆還可以分為新生代和老年代,新生代又分為 Eden 空間、From Survivor空間、ToSurvivor空間(后續(xù)再GC回收時會詳細講解)。

3、虛擬機棧
? ? ? ? 虛擬機棧是線程私有的,它的生命周期和線程的生命周期一樣。Java虛擬機棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個方法在執(zhí)行的時都會創(chuàng)建一個棧幀(Stack Frame)用于存儲局部變量表、操作數(shù)棧、動態(tài)鏈接和方法出口等信息。每個方法從調(diào)用到執(zhí)行完成的過程就對應著幀幀在虛擬機棧中入棧和出棧過程。
? ? ? ? 局部變量表存放了編譯期可知的各種基本數(shù)據(jù)類型(Boolean、byte、char、short、int、float、long、double)、對象引用(reference類型, 它不等于對象本身,可能是一個指向?qū)ο笃鹗嫉刂返囊弥羔?,也可能是指向一個代表對象的句柄或其他與此對象相關的位置)。
? ? ? ? 其中64位長度的 long 和 double 類型的數(shù)據(jù)會占用 2 個 局部變量空間(Slot),其余數(shù)據(jù)類型只占一個,當進入一個方法時,這個方法需要在棧幀中分配多大的局部變量空間是完全確定的,在方法運行期間不會改變局部變量表的大小。
? ? ? ?在Java虛擬機規(guī)范中,對這個區(qū)域規(guī)定了兩種異常情況:如果線程請求的棧深度大于虛擬機所允許的最大深度,將拋出 StackOverflowError 異常;如果虛擬機可以動態(tài)拓展,如果拓展是無法申請到足夠的內(nèi)存,將拋出 OutOfMemoryError 異常。

4、本地方法棧
? ? ? ? 本地方法棧和虛擬機棧發(fā)揮的作用是幾乎是一樣的,唯一的區(qū)別在于虛擬機棧位虛擬機執(zhí)行Java方法服務,本地方法棧位虛擬機使用到的 Native 方法服務。
5、程序計數(shù)器
? ? ? ? 程序計數(shù)器是一塊較小的內(nèi)存空間,可以看做是當前線程所執(zhí)行的字節(jié)碼的行號指示器。字節(jié)碼解釋器工作時就是通過改變這個計數(shù)器的值來選取嚇一跳需要執(zhí)行的字節(jié)碼指令。分支、循環(huán)、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數(shù)器來完成。
? ? ? ? 由于Java虛擬機的多線程是通過線程輪流切換并分配處理器執(zhí)行時間的方式來實現(xiàn)的,在任何一個確定的時刻,一個處理器都只會執(zhí)行一條線程中的指令。因此,為了線程切換后能恢復到正確的執(zhí)行位置,每條線程都需要有一個獨立的程序計數(shù)器,各個線程之間互不影響,獨立存儲。如果線程正在執(zhí)行一個Java方法,這個計數(shù)器記錄的是正在執(zhí)行的虛擬機自己餓啊指令的地址;如果正在執(zhí)行的是 Native 方法,這個計數(shù)器的值位 空 (Undefined)。
? ? ? ? 總結:JVM基礎篇就說到這里,主要是為后續(xù)的博文在鋪墊。通過這篇博文我們知道了JVM的大概分區(qū)以及各分區(qū)的作用,后續(xù)我們將會圍繞著從程序運行的角度出發(fā)去詳細探討各區(qū)域的詳細流程。