1. 程式人生 > >深入理解JVM:JVM執行時數據區域分類

深入理解JVM:JVM執行時數據區域分類

return 位置 工作 () 對象 地方法 存在 utm 連續

JVM在運行java程序的過程中會把他所管理的內存劃分為若幹個不同的數據區域。

這些區域都有各自的用途和創建、銷毀時間。有些區域隨著虛擬機的啟動而存在。有些區域則依賴用戶線程的啟動和結束而建立和銷毀。依據《Java虛擬機規範1.7》規定,Java虛擬機所管理的內存分為下面幾個區域:
程序計數器、Java虛擬機棧、本地方法棧、Java堆、方法區、運行時常量池、直接內存
程序計數器
是一塊較小的內存空間。他能夠看作是當前線程所運行的字節碼的行號指示器。

在虛擬機的概念模型裏,字節碼解釋器工作時就是通過改變這個計數器的值來選取下一條須要運行的字節碼指令,分支,循環,跳轉,異常處理,線程恢復等基礎功能都依靠這個計數器來完畢。


因為Java虛擬機的多線程是通過線程輪流切換並分配處理器運行時間的方式來實現的,在不論什麽一個確定的時刻,一個處理器(對於多核處理器僅僅有一個內核)都僅僅會運行一個線程中的指令。

因此,為了線程切換後能恢復到正確的運行位置,每條線程都須要有一個獨立的程序計數器。各條線程之間計數器互不影響,獨立存儲。我們稱這類內存區域為“線程私有”內存。


Java虛擬機棧
Java虛擬機棧也是線程私有的。他的生命周期與線程同樣。虛擬機棧描寫敘述的是Java方法運行的內存模型:每個方法在運行的同一時候都會創建一個棧幀用於存儲局部變量表、操作數棧、動態鏈接、方法出入口等。

每個方法從調用到運行完畢過程。就相應著一個棧幀在虛擬機中入棧到出棧的過程。
常常有人將Java內存分為堆內存(Heap)和棧內存(Stack),這樣的分法是比較粗略的,java的內存區域遠比這個復雜。
局部變量表存放了編譯期可知的各種數據類型(boolean,byte,char,shot,int,float,long,double),對象引用類型(reference類型,他不同於對象本身,可能是一個指向對象起始地址的引用指針。也可能是一個代表對象的句柄或者其它與此對象關聯的位置)和returnAddress類型(指向了一條字節碼指令的地址)。
當中64位長度的lang和double類型的數據會占用2個局部變量空間(slot),其余數據類型僅僅占用1個。局部變量表所需的內存空間在編譯期間完畢分配。當進入一個方法時。這種方法須要在幀中分配多大的局部變量空間是全然確定的。在方法運行期間不會改變局部變量表的大小。


在java虛擬機規範中,對於這個區域規定了兩種異常狀況:假設線程請求的棧深度大於虛擬機所同意的深度,將拋出StackOverflowException異常;假設虛擬機能夠動態擴展。假設擴展到無法申請足夠的內存。就會拋出OutMemoryError異常。
本地方法棧
虛擬機棧是為虛擬機運行Java方法也就是字節碼服務的。而本地方法棧則為虛擬機用的Native方法服務。與虛擬機棧一樣。本地方法棧也會拋出StackOverflowException和OutMemoryError異常。
Java堆
對於大多數應用來說。Java堆是Java虛擬機所管理的內存最大的一塊,Java堆是被全部線程共享的一塊內存區域,在虛擬機啟動時創建。此內存區域的唯一目的就是存放對象實例,差點兒全部的對象實例都要在這裏分配內存,全部的對象實例以及數組都要在堆上分配,隨著編譯器的發展全部的對象都在對上分配變得不是那麽絕對了。


Java堆是垃圾收集器管理的主要區域。因此非常多時候被稱作GC堆。

從內存回收的角度來看:因為如今收集器基本都採用分代收集算法,所以Java堆還能夠細分為:新生代和老年代。再仔細一點有Eden空間、From Survivor空間、To Survivor空間等。從內存的分配角度來看,線程共享的java堆可能劃分出多個線程私有的分配緩沖區。

只是不管怎樣劃分都與存放內容無關。不管哪個區域存儲的依舊是實例對象。進一步劃分的目的是為了更好的回收內存,或者更快的回收內存。


依據Java虛擬機的規定,Java堆能夠處於物理上不連續的內存空間,僅僅要邏輯上連續的就可以。


方法區
方法區與Java堆一樣。是各個線程共享的區域,他用於存儲已被虛擬機載入的類信息。常量,靜態變量。即時編譯器編譯後的代碼等數據。盡管java虛擬機規範把方法區描寫敘述為堆的一個邏輯部分,可是他有別名——非堆,為了與java堆區分開來。
對於習慣在Hotspot虛擬機上開發、部署的開發人員來說,非常多人願意把方法區稱為永久代,本質上兩者並不等價。

依據java虛擬機規範當方法區無法滿足內存分配需求時將拋出OutOfMemoryError異常。
運行時常量池
是方法區的一部分,Class文件除了有類的版本號、字段、方法、接口等描寫敘述信息外,另一項信息是常量池,用於存放編譯期生成的各種字面量和符號引用,這部分內容將在類載入後進入方法區的運行時常量中存放。

他也是方法區的一部分,無法滿足內存分配需求時將拋出OutOfMemoryError異常。
直接內存
不是虛擬機運行時數據區一部分,也不是java虛擬機規範中定義的內存區域。可是這部分內存也被頻繁的使用,並且可能導致OutOfMemoryError異常。直接內存的分配不會受到java堆大小的限制,可是,既然是內存。肯定還是會受到本機內存的限制。運維在配置虛擬機參數時,會依據實際內存設置-Xmx等參數信息,但常常忽略直接內存。使得內存區域總和大於物理內存限制,從而導致動態擴展時出現OutOfMemoryError異常。

深入理解JVM:JVM執行時數據區域分類