轉載請以鏈接形式標明出處:
本文出自:103style的博客
JAVA運行時數據區域
主要包括:
線程共享 的數據區:方法區、堆。
線程私有 的數據區:程序計數器、虛擬機棧、本地方法棧。
如下圖:
程序計數器
程序計數器 Program Counter Register
是一塊較小的內存空間,它可以看作是當前線程所執行的字節碼的行號指示器。在虛擬機的概念模型裏,字節碼解釋器工作時就是通過改變這個計數器的值來選取下一條執行字節碼指令。
由於Java虛擬機是通過線程輪流切換並分配處理器執行時間的方式來執行一條線程的指令。所以爲了線程切換後能恢復到正確位置,每條線程都有一個獨立的程序計數器。 各線程之間的程序計數器互不影響,獨立存儲,我們稱這類內存爲 線程私有 的內存。
如果執行的是 Java
方法,這個計數器記錄的是正在執行的虛擬機字節碼指令地址。如果是 native
方法,計數器爲空。此內存區域是唯一一個在java虛擬機規範中沒有規定任何 OutOfMemoryError
情況的區域。
Java虛擬機棧
與 程序計數器 一樣,Java虛擬機棧 也是線程私有,生命週期 和線程相同。
Java虛擬機棧 描述的是 Java 方法執行的內存模型:每個方法在執行的同時都會創建一個棧幀(Stack Frame)用於存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。
一個方法從開始到結束對應一個棧幀入棧到出棧的過程。
局部變量表存放了各種基本類型、對象引用和 returnAddress
類型(指向了一條字節碼指令地址)。
其中 64
位長度 long
和 double
佔 兩個
局部變量空間Slot
,其他只佔 一個
。
局部變量表的內存空間在編譯期間完成分配,所以在運行期間不會改變其大小。
Java虛擬機棧 規定的異常情況有兩種:
- 線程請求的棧的深度大於虛擬機所允許的深度,將拋出
StackOverflowError
異常. - 如果虛擬機可以動態擴展,如果擴展時無法申請到足夠的內存,就拋出
OutOfMemoryError
異常.
本地方法棧
和 Java虛擬機棧 很類似,不同的是 本地方法棧 爲 Native
方法服務。
有的虛擬機(eq: Sun HotSpot )
則把 Java虛擬機棧 和 本地方法棧 合二爲一。
Java堆
對大多數應用來說,
Java堆是Java虛擬機所管理的內存中最大的一塊。由所有線程共享,在虛擬機啓動時創建。堆區唯一目的就是存放對象實例。
Java堆 是 垃圾收集器 管理 的 主要區域 ,也稱 GC堆 。
從內存回收角度來看,由於現在的收集器都採用 分代收集算法,Java堆 可以細分爲 新生代 和 老年代。再細分可分爲 Eden空間、From Survivor空間、To Survivor空間。
Java堆 可以處於物理上不連續的內存空間,只要邏輯上連續即可。
在實現時,既可以是固定大小,也可以是可擴展的。
目前主流的虛擬機都是可擴展的,通過 -Xmx 和 -Xms 控制。堆無法擴展時,拋出 OutOfMemoryError
異常.
方法區
方法區 是所有線程共享的內存區域,存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。
雖然 Java虛擬機規範 把 方法區 描述爲 堆的一個邏輯區域,但是它有一個別名叫做 Non-Heap
(非堆),目的應是區分與Java堆 。
對於在 HotSpot 上開發的開發者來說,願意把 方法區 稱爲 永久代,本質上兩者並不等價,僅是因爲 HotSpot 把 GC分代收集算法 擴展到 方法區,或者說使用 永久代 來實現 方法區 而已。
Java虛擬機規範 對 方法區 的限制非常寬鬆,除了和 Java堆 一樣 不需要連續的內存區域 和 可以選擇固定大小或者可擴展 外,還可以選擇 不實現垃圾收集。 相對而已,在這個區域的垃圾收集行爲較少出現。
當 方法區 無法滿足內存分配需求時,拋出 OutOfMemoryError
.
運行時常量池
運行時常量池 是 方法區 的一部分,Class
文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項是常量池Constant Pool Table
,用於存放編譯期生成的各種字面量和符號引用。這部分將在 類加載後 進入 方法區 的 運行時常量池。
運行時常量池 相對於 Class
文件常量池的另外一個重要特徵是具備動態性,Java語言並不要求常量一定只有編譯期才能產生。運行期間也可能將新的常量放入池中,這種特性被開發人員利用得比較多的便是 String
類的 intern()
方法。
既然運行時常量池 是 方法區 的一部分,自然受到 方法區 內存的限制,無法滿足內存分配需求時,拋出OutOfMemoryError
.
直接內存
直接內存 並不是虛擬機運行時數據區的一部分,也不是 Java虛擬機規範 中定義的內存區域。
但是這部分內存也被頻繁的使用,而且也可能導致 OutOfMemoryError
。
JDK1.4 加入了 NIO
,引入一種基於 通道Channel
與 緩衝區Buffer
的I/O方式,它可以使用 Native函數庫
直接分配堆外內存,然後通過一個存儲在Java堆中的 DirectByteBuffer
對象作爲這塊內存的引用進行操作。因爲避免了在 Java堆和Native堆中來回複製數據,提高了性能。
顯然 直接內存 的分配不會受到 Java堆 大小的限制,但是會受到本機總內存的限制,當各個內存區域總和大於物理內存限制,拋出 OutOfMemoryError
異常。
如果覺得不錯的話,請幫忙點個贊。
以上
掃描下面的二維碼,關注我的公衆號 Android1024, 點關注,不迷路。