java運行時數據區圖:
一共分爲5部分,線程私有部分有程序計數器、本地方法棧、虛擬機棧;線程共享部分有堆、方法區(運行時常量池);還有一部分內存區域也常使用–直接內存,但不屬於運行時數據區。除了程序計數器之外其他區域都可能會出現OOM(OutOfMemoryError)。下面,對這些區域進行詳細介紹。
程序計數器
- 當前線程所執行的字節碼的行號指示器。 字節碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基本功能都需要依賴這個計數器來完成。
- 屬於線程私有。
- 爲了保證線程切換後能夠恢復到爭取的執行位置,每天線程需要有一個獨立的程序計數器,各條線程之間互不影響。
- 執行java方法和Native方法區別:
- 執行Java方法,記錄的是正在執行的虛擬機字節碼指令的地址;
- 執行Native方法,計數器的值爲空。
- 唯一一個在java虛擬機規範中沒有規定任何OutOfMemoryError情況的區域。
Java虛擬機棧
- 描述的是java方法執行的內存模型。
- 每個方法執行的時候都會創建一個棧幀用於存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。每個方法從調用直至執行完成的過程,對應着一個棧幀在虛擬機棧中入棧到出棧的過程。
- 異常情況:
- 如果線程請求的棧深度大於虛擬機所允許的深度,拋出StackOverflowError;
- 如果虛擬機棧可以動態擴展,擴展時無法申請到足夠的內存,就會拋出OutOfMemoryError。
本地方法棧
- 服務Native方法。
- 拋出異常與虛擬機棧一樣。
java堆
- java虛擬機所管理的內存中最大的一塊。此內存區域唯一目的:存放對象實例。幾乎所有的對象實例都在這裏分配。
- 垃圾收集器管理的主要區域,即“GC堆”。
- 可以處於物理上不連續的內存空間中,只要邏輯上連續即可。可是設置參數設置固定大小或者可擴展。
- 在堆中沒有內存完成示例分配,並且對也無法在擴展時,將會拋出OutOfMemoryError。
方法區
- 存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。
- 和java堆一樣可不連續、固定或者擴展。
- 垃圾收集行爲在此區域比較少出現,回收目標主要是針對常量池的回收和堆類型的卸載。
- 無法滿足內存分配需求時,將拋出OutOfMemoryError。
運行時常量池
- 屬於Class文件的一部分,存放編譯器生成的各種字面量和符號引用。
- Class文件:版本、字段、方法、接口、常量池等信息。
- java虛擬機對Class文件每一部分的格式都有嚴格規定,每一個字節用於存儲那種數據都必須符合規範上的要求才會被虛擬機認可、裝載和執行,但對運行時常量池沒有任何細節的要求。
- 具有動態性,java並不要求常量一定只有編譯器才能產生。
- 會拋出OutOfMemoryError。
直接內存
- 不是虛擬機運行時數據區的一部分,也不是java虛擬機規範中定義的內存區域。
- 在JDK1.4中新加入NIO類,引入了基於通道(Channel)和緩衝區(buffer)的I/O方式,它可以使用native函數庫直接分配堆外內存。
- 受本機總內存大小以及處理器尋址空間的限制。動態擴展時會出現OutOfMemoryError。