經常在說JVM內存分布,也經常去看,但是總是在面試的時候說不清楚或者模糊,甚至有可能說錯,只有真正的理解,并且在心中有一個總結構圖才能記得清楚說的清楚!
| JVM總覽圖
java內存區域主要分程序計數器、Java虛擬機棧、本地方法棧、Java堆、方法區、直接內存。其中程序計數器、Java虛擬機棧、本地方法棧屬于線程隔離,即他們都有自己的線程歸屬,其他屬于線程共享的。
| 各分區詳解
a、程序計數器
這個是當前線程正在執行的字節碼行號指示器。根據這里面的內存數據來確定程序接下來執行的指令。每個線程都有一個,相互隔離,線程切換回來時才知道怎么執行。如果執行的是方法,這里記錄的是虛擬機字節碼指令的地址。當執行的是Native方法的時候為空(Undefined)。
因為只存儲一個指令,所以它不會出現任何OutOfMemoryError。也是唯一一個!
b、Java虛擬機棧
每個線程私有,里面裝的多個棧幀,每個棧幀對于的一個方法。里面存儲的是Java方法的內存模型。相當于描述的是一個方法需要的內容。
如下圖,每個線程都有一個虛擬機棧,每個棧中都有多個棧幀。每個棧幀代表一個方法,一個方法的執行就是棧幀的進棧與出棧。
比如一個main方法調用了Method1(),Method1()調用Method2()。當線程執行的時候,先main方法對應的棧幀壓入虛擬機棧,作為棧幀1。然后調用Method1()時,Method1()對應的棧幀壓入虛擬機棧,作為棧幀2。然后調用Method2()時對應的棧幀壓入虛擬機棧作為棧幀3。當Method2()方法執行完成,棧幀3彈出。接著Method1()繼續執行,執行完成棧幀2彈出,接著main方法繼續執行,執行完成棧幀1彈出。整個方法執行完成。
每個棧幀存就是對方法的描述。其中局部變量表就是一個方法里面定義的變量,其中包括:基本數據類型(boolean、byte、char、short、int、float、long、double)和對象引用。
注:long和double會占兩個局部變量空間。
異常:線程請求的棧深度大于虛擬機允許的深度,將拋出StackOverflowError異常。如果虛擬機棧可以動態擴展,當擴展的時候沒有申請到內存的時候拋出OutOfMemoryError.
c、本地方法棧
線程私有,和上一個Java虛擬機棧作用相似,Java虛擬機棧是為Java方法服務,本地方法棧是為Native服務。
d、Java堆
Java虛擬機管理最大的一塊,線程共享,存放對象實例和數組。分新生代(1/3)和老年代(2/3),新生代還可以分Eden(8/10)、From Survivor(1/10) 、To Survivor(1/10),是主要根據垃圾清理來分的。
異常:無法再對對象實例分配,并且堆也無法擴展時,將拋出OutOfMemoryError。
e、方法區
線程共享,主要存儲被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼。
運行時常量池也是方法區的一部分,比如String有一個常量池,他就是放到這個里面的。
異常:當方法區無法滿足內存分配時,將拋出OutOfMemoryError異常。
f、直接內存
NIO通過使用Native函數庫直接分配對外內存。
異常:不受Java堆大小限制,但是受機器的物理內存限制,當各個內存區域大于機器物理內存的時候,會出現OutOfMemoryError。
| 總結
Java作為面向對象的一門語言,表面上是JVM內存分布以線程劃分,實則主要也是類和對象的分布。簡單的總結對比圖:
Java程序員日常學習筆記,如理解有誤歡迎各位交流討論!