JVM執行時記憶體區域劃分
JVM執行時的記憶體區域分為:程式計數器,虛擬機器棧,本地方法棧,堆,方法區,執行時常量池以及直接記憶體區域
線面主要來分別說下這些區域的儲存內容作用
1.程式計數器
程式計數器是執行緒私有的,當前執行緒所執行的位元組碼行號指示器。位元組碼解析器通過改變這個計數器來選取下一條需要執行的位元組碼指令。
執行java方法的時候,儲存的是正在執行的虛擬機器位元組碼指令地址,執行本地方法的時候儲存的是空
在jvm記憶體模型中唯一一個沒有OOM異常的區域
2.虛擬機器棧
也是執行緒私有的,生命週期與執行緒相同。此塊區域描述的是方法執行的記憶體模型。每一個方法從呼叫到執行完成在虛擬機器棧中描述的都是棧幀的入棧和出棧,虛擬機器棧中的儲存的是區域性常量表,運算元棧,動態連結和方法出口等
很多時候,我們在描述jvm記憶體的時候,喜歡說棧和堆,這樣的描述是很粗糙的,而這裡的棧就是指我們的虛擬機器棧
虛擬機器棧中會丟擲stackOverFlowError異常和OOM異常。執行緒請求的棧的深度大於虛擬機器允許的棧的深度的時候,會丟擲SOF異常。同樣的;當擴充套件的時候無法申請到足夠的記憶體的時候,會丟擲OutOfMemoryError異常
注意兩個知識點:區域性變量表中儲存有基本資料型別,物件引用和returnAddress型別;long和double佔用2個區域性變數空間,其他的佔用一個區域性變數空間
3.本地方法棧
本地方法棧與虛擬機器棧類似。差別在於,本地方法棧為虛擬機器的本地方法服務。在jvm規範中,本地方法棧中的方法實現和資料型別沒有做嚴格的規定,所以本地方法棧可以自由的實現,包括不使用java語言。在Hotspot中,本地方法棧被合併到了虛擬機器棧中。本地方法棧也會丟擲StackOverFlowError異常和OutOfMemoryError異常
4.java堆
java堆是jvm中最大的一塊區域,此區域是共享的,在虛擬機器初始化的時候建立的。java堆中儲存著具體的java物件例項,幾乎所有的物件例項都儲存在這個區域中。java虛擬機器規範中規定,所有的陣列和物件例項都儲存在java堆中,但是隨著JIT編譯器的發展和逃逸分析技術的逐漸成熟,這個也成了並非絕對的。
jvm規範中,java堆在實體記憶體上並不一定要是連續的,只要保證在邏輯上是連續的即可
java堆的大小可以通過-Xmx和-Xmn來設定最大最小值,當然也可以設定成固定大小的
在這個區域中,當沒有足夠記憶體分配物件的時候(GC之後),則會丟擲OutOfMemoryError異常
5.方法區
方法區與java堆一樣,是執行緒共享的區域,此區域中儲存著類資訊,變數,靜態變數,即時編譯器編譯後的程式碼等資料。在java虛擬機器規範中,也將方法區描述為java堆的一部分,但是方法區還有另一個名字叫Non-Heap,目的也是為了與java堆區分開
hotSpot上,習慣將這個區域叫永久帶,因為hotspot團隊將GC分代收集擴充套件到這個區域,或者是使用永久帶來實現了方法區,但是本質上這兩者是不能對等的
java虛擬機器規範對於方法區的限制也是很寬鬆的,如儲存的不需要物理上連續的,只要邏輯上連續即可,與java堆一樣。
這個區域的GC是很少的,因為要回收的內容要求太苛刻,造成回收的效率不高,但是並不意味著就不進行GC,只是回收的成績不夠滿意
此區域也會出現OutOfMemoryError異常
6.執行時常量池
執行時常量池是方法區的一部分,儲存著編譯期生成的各種字面量和符號引用
執行時常量池相對於class檔案常量池,多了動態性。java並不要求常量只有在編譯器產生,執行期間也有可能將常量寫入執行時常量池中,如String的intern方法
此區域與方法區一樣,會跑村OutOfMemoryError
7.直接記憶體
直接記憶體並不是虛擬機器執行時資料區域的一部分,也不是java虛擬機器規範中的記憶體區域 自jdk1.4之後,加入了NIO,引入
了一種基於通道與快取區的I/O方式,可以使用直接記憶體,提高了效率,因為使用直接記憶體便不用將物件從直接記憶體拷貝到堆中一樣,減少了互動,提升了體驗
此區域也會丟擲OutOfMemoryError異常
添加個思維導圖供參考,裡面沒有列出直接記憶體和執行時常量池