1. 棧的存儲
每個線程都有自己的棧,棧中數據以棧幀(Stack Frame)為基本單位
線程上正在執行的每個方法都各自對應一個棧楨(Stack Frame)
棧楨是一個內存區塊,是一個數據集,維系著方法執行過程中的各種數據信息
JVM 對棧的操作有兩個:壓棧與出棧,遵循“新進后出”或“后進先出”原則
1.1 當前棧幀
一條活動線程中,一個時間點上,只會有一個活動的棧幀。
即當前執行的方法的棧幀(棧頂棧幀)是有效的,被稱為當前棧幀。
1.2 當前方法
與當前棧幀對應的方法是當前方法
1.3 當前類
當前方法所在的類就是當前類
1.4 代碼測試
執行引擎運行的所有字節碼指令只針對當前棧幀操作。
若在該方法中調用了其他方法,對應新的棧幀就會被創建出來,
放在站的頂端,成為新的棧幀
測試:方法 1 調用方法 2 ,方法?2 調用 3 ,方法 3 結束;
調用方法 1 時,方法 1 對應的棧幀為當前棧幀;
調用方法 2?時,方法 2?對應的棧幀為當前棧幀;
調用方法 3?時,方法 3?對應的棧幀為當前棧幀;
方法 3 執行完畢,方法 2 對應的棧幀為當前棧幀;
方法 2?執行完畢,方法 1?對應的棧幀為當前棧幀;
public class StackStruTest {public static void main(String[] args) {StackStruTest stackStruTest = new StackStruTest();stackStruTest.method1();}public void method1(){System.out.println("method1 開始執行");method2();System.out.println("method1 執行結束");}public void method2(){System.out.println("method2 開始執行");method3();System.out.println("method2 執行結束");}public void method3(){System.out.println("method3 開始執行");System.out.println("method3 執行結束");}
}
method1 開始執行
method2 開始執行
method3 開始執行
method3 執行結束
method2 執行結束
method1 執行結束
1.5 運行原理
不同線程中包含的棧幀不允許相互引用
方法的結束(棧幀彈出):
? ? ? ? ① 正常結束,以 return 為代表
? ? ? ? ② 異常結束:方法執行中出現未捕獲處理的異常,以拋出異常的方式結束
2. 棧的內部結構
棧幀入棧:表示方法調用
棧幀出棧:表示方法結束
棧幀是有大小的,
其大小取決于棧幀內部的結構
棧幀五大部分:
? ? ? ? 局部變量表(Local Variables)
? ? ? ? 操作數棧(Operand Stack)(或表達式棧)
? ? ? ? 動態鏈接(Dynamic Linking)(或指向運行時常量池的方法引用)
? ? ? ? 方法返回地址(Return Address)(或方法正常退出/異常退出的定義)
? ? ? ? 一些附加信息
2.1 局部變量表?
又被稱為局部變量數組或本地變量表
定義為一個數字數組,主要用于存儲方法參數和定義在方法體內的局部變量
局部變量表存儲在建立線程的棧上,每個方法都有對應的各自的棧幀,因此不存在數據安全問題
局部變量表所需的容量大小是在編譯期確定下來的
方法嵌套調用的次數由棧的大小決定。一般來說,棧越大,方法嵌套調用次數越多
局部變量表中的變量只在當前方法調用中有效
當方法調用結束后,隨著方法棧楨的銷毀,局部變量表也會隨之銷毀
3. 堆
一個 JVM 實例 只存在一個堆內存,堆也是 Java 內存管理的核心區域
Java 堆區在 JVM?啟動的時候被創建,其空間大小也就確定
堆是 JVM 管理的最大一塊內存空間
? ? ? ? 堆的大小可以調節
堆可以處于物理上不連續的內存空間中,但在邏輯上它應該被視為連續的
所有的線程共享 Java 堆,堆里還可以劃分線程私有的緩沖區(Thread Local Allocation?Buffer,TLAB)[多個線程共享堆,易出現并發性,為避免,可再劃分為緩沖區]
幾乎所有的對象實例以及數組都應當在運行時分配在堆上。
數組和對象可能永遠不會存儲在棧上;
在方法結束后,堆中的對象幾乎不會被馬上移除,僅僅在垃圾收集的時候才會被移除
堆,是GC(global collection,垃圾回收器),執行垃圾回收的重點區域
3.1 內存細分
新生區 == 新生代 == 年輕代
養老區 == 老年區?== 老年代
永久區 = 永久代
堆空間暫時只包含兩部分:新生代,老年代