學習Java不可避免的要學習Java虛擬機,也就是JVM,Java虛擬機規范裡面規定了程序運行期間會使用到的運行時數據區,這也是JVM的內存區域劃分的模型,應該可以這麼理解吧。
這其中有一些是隨著虛擬機的啟動和退出而創建和銷毀的,這些區域是線程共享的,另外有一些是跟各個線程綁定的,隨著線程的開始和結束而創建和銷毀,我們可以稱這些區域是線程私有的。
根據《Java虛擬機規范Java SE 7版》和《Java虛擬機規范Java SE 8版》的規定,我們可以劃分出如下幾個運行時數據區,如下圖所示:
在Java虛擬機中,堆是可供各個線程共享的運行時內存區域,也是供所有類實例和數組對象分配內存的區域。這塊區域隨著虛擬機的啟動而創建,它的唯一使命就是存放對象實例,這塊區域也是GC主要關注的地方。
下面的就是我的筆記本上的JVM堆的劃分情況,可以看到分為新生代、老年代和永久代,新生代裡面有可以分為Eden Space、From Survivor Space和To Survivor Space。
Heap Usage:
PS Young Generation
Eden Space:
capacity = 17301504 (16.5MB)
used = 2483088 (2.3680572509765625MB)
free = 14818416 (14.131942749023438MB)
14.351862127130682% used
From Space:
capacity = 2621440 (2.5MB)
used = 2615312 (2.4941558837890625MB)
free = 6128 (0.0058441162109375MB)
99.7662353515625% used
To Space:
capacity = 6291456 (6.0MB)
used = 0 (0.0MB)
free = 6291456 (6.0MB)
0.0% used
PS Old Generation
capacity = 44564480 (42.5MB)
used = 13316368 (12.699478149414062MB)
free = 31248112 (29.800521850585938MB)
29.88112505744485% used
PS Perm Generation
capacity = 22020096 (21.0MB)
used = 14907008 (14.2164306640625MB)
free = 7113088 (6.7835693359375MB)
67.6972888764881% used
根據虛擬機規范的規定,Java堆可以是固定的大小也可以是按照需求動態擴展的,而且不需要保證是連續的。
存放內容:所有的對象實例和數組。
方法區是一個線程共享的區域,它用於存儲已被虛擬機加載的類信息、常量、靜態變量。方法區是堆的邏輯組成部分,Hotspot用永久代實現了方法區。
方法區還包含運行時常量池,用於存放編譯時生成的各種字面量和符號引用,但是不要求常量一定是在編譯時期產生的,運行期間也可以將新的常量放入池中,比如String的intern()方法便是利用了這一特性。
存放內容:類的結構信息,如類的字段、方法、接口、構造函數,還有運行時常量池等。
這塊區域是每個線程獨立擁有的,也就是線程私有的,我們可以把它看作是當前線程所執行的字節碼的行號指示器。
這塊區域時虛擬機規范裡面唯一一個沒有規定任何OutOfMemoryError情況的區域。
存放內容:如果線程執行的是一個Java方法,那麼寄存器裡面記錄的就是正在執行的虛擬機字節碼指令的地址,如果線程執行的是一個native方法,那麼寄存器記錄的值為undefined。
虛擬機棧也是線程私有的內存區域。每個方法在執行的時候都會創建一個棧幀用於存儲局部變量表、操作數棧、方法出口等信息,每一個方法從調用到執行完成就是一個棧幀入棧和出棧的過程。
局部變量表存放了編譯時期可知的各種基本數據類型、對象引用和指向了一條字節碼指令的地址。
存放內容:局部變量表、操作數棧、方法出口等信息。
和虛擬機棧類似,存儲Native方法的相關信息。
存放內容:局部變量表、操作數棧、方法出口等信息。