1、程序計數器:
程序計數器是線程私有的內存,JVM多線程是通過線程輪流切換並分配處理器執行時間的方式實現的,當線程切換后需要恢復到正確的執
行位置(處理器)時,就是通過程序計數器來實現的。此內存區域是唯一 一個在JVM規范中沒有規定任何OutOfMemoryError情況的區域。
2、Java虛擬機棧:
Java虛擬機棧也是線程私有的,它的生命周期與線程相同,Java虛擬機棧為JVM執行的Java方法(字節碼)服務。每個Java方法在執行時都
會創建一個棧幀,用於存儲局部變量表、操作數棧、動態鏈表、方法出口等信息。
局部變量表存放的是基本數據類型,對象引用和returnAddress類型。也就是說基本數據類型直接在棧中分配空間;局部變量(在方法或者
代碼塊中定義的變量)也在棧中分配空間,當方法執行完畢執行完畢后該空間會被JVM回收;引用數據類型,即我們new創建的對象引用,JVM
會在棧空間給這個引用分配一個地址空間(門牌號),在堆空間給該對象分配一個空間(家),棧空間的地址引用指向堆空間的對象(通過門牌
號找到家)。
在這個區域,JVM規范規定了兩個異常狀況:
a.如果線程請求的棧深度大於JVM所允許的深度,將拋出StackOverflowError異常;
b.如果虛擬機棧可以動態擴容(大部分JVM都可以動態擴容),如果擴展時無法申請到足夠的內存,就會拋出OutOfMemoryError異常。
3、本地方法棧:
和Java虛擬機棧作用相似,只是本地方法棧為JVM使用到的Native(本地)方法服務,它也會拋出StackOverflowError和OutOfMemoryError異常。
參考:OutOfMemoryError/OOM/內存溢出異常實例分析--虛擬機棧和本地方法棧溢出
4、Java堆:
JVM內存中最大的一塊,是所有線程共享的區域,在JVM啟動時創建,唯一目的就是用來存儲對象實例的,也被稱為GC堆,因為這是垃圾收集器
管理的主要區域。Java堆還可分為:新生代和老年代,其中新生代還可再分為:Eden:From Survivor:To Survivor = 8:1:1,有面試官會問
為什么8:1:1,可參考:垃圾收集算法——標記-清除算法、復制算法、標記-整理算法、分代收集算法 中的復制算法
通過一張圖來看一下如何通過參數來控制各區域的大小
控制參數
-Xms設置堆的最小空間大小。
-Xmx設置堆的最大空間大小。
-XX:NewSize設置新生代最小空間大小。
-XX:MaxNewSize設置新生代最大空間大小。
-XX:PermSize設置永久代最小空間大小。
-XX:MaxPermSize設置永久代最大空間大小。
-Xss設置每個線程的堆棧大小。
JVM規范中規定,Java堆可以處於邏輯上連續,物理上不連續的內存空間中,就像我們的磁盤一樣。如果在堆中沒有內存分配給對象實例,並且
堆也無法擴展,也會拋出OutOfMemoryError異常。
參考:OutOfMemoryError/OOM/內存溢出異常實例分析--堆內存溢出
5、方法區:
和Java堆一樣,是各個線程共享的內存區域,它用於存儲已被JVM加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據,別名叫Non-Heap(非堆)。
很多人稱他為“永久代”,本質上兩者並不等價,只是因為HotSpot把GC擴展到了方法區,或者說使用永久代來實現方法區而已。方法區除了和Java堆一樣不需要連續
的內存和可以選擇固定大小或者可擴展外,還可以選擇不實現垃圾收集。JVM規范規定,當方法區無法滿足內存分配需求時,將會拋出OutOfMemoryError異常。