Jvm圖解

運行時數據區

Java虛擬機在執行Java程序的過程中會把它所管理的內存劃分爲若干個不同的數據區域,這些區域都擁有自己的用途,並隨着JVM進程的啓動或者用戶線程的啓動和結束建立和銷燬。

先讓我們瞭解下進程和線程的區別:

進程是資源分配的最小單位,線程是程序執行的最小單位。

進程有自己的獨立地址空間,每啓動一個進程,系統就會爲它分配一個地址空間、建立數據表來維護代碼段、堆棧段和數據段,這種操作非常昂貴。而線程是共享進程中的數據的,使用相同的地址空間,因此CPU切換一個線程的花費要比進程小很多,同時創建一個線程的開銷也要比進程小很多。

同一個進程中可以包括多個線程,並且線程共享整個進程的資源(寄存器、堆棧、上下文),一個進程至少包含一個線程。線程之間的通信更加方便,同一進程下的線程共享全局變量、靜態變量等數據,而進程之間的通信則需要以通信的方式(IPC)進行。

程序計數器(Program Counter Register)

程序計數器(Program Counter Register)是一塊較小的內存空間,它可以看成是當前線程所執行字節碼的行號指示器。在計算機中,其實程序計數器就是一個寄存器,依據不同計算機細節的差異,它可以存放當前正在被執行的指令,也可以放下一個被執行的指令。

在虛擬機的概念模型中,字節碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令的

由於Java虛擬機的多線程是通過線程輪詢切換並分配處理器執行時間的方式來實現的,在任何一個確定的時候,一個處理器都只會執行一條線程中的指令,因此爲了線程切換之後能過恢復到正確的執行位置,每條線程都需要擁有一個獨立的程序計數器,各條線程之間的計數器互不影響,獨立存儲,所以程序計數器是線程私有的內存,也就是它屬於線程隔離區的。

如果線程執行的是一個Java方法,這個計數器記錄的就是正在執行的虛擬機字節碼指令地址;如果正在執行的是Native方法,那麼這個計數器的值就是(Undefined)。

此內存區域是唯一一個在Java虛擬機規範中沒有規定任何OutOfMemoryError情況的區域。

Java虛擬機棧

Java虛擬機棧(Java Virtual Machine Stack)也是線程私有的,即他的生命週期和線程相同。

在Java中,JVM中的棧記錄了線程的方法調用,每個線程擁有一個棧,在某個線程的運行過程中,如果有新的方法調用,那麼該線程對應的棧就會增加一個存儲單元,即棧針(Stack Frame)。

虛擬機棧描述的是Java方法執行的內存模型:每個方法在執行時都會創建一個棧針(Stack Frame)用於存儲局部變量表、操作數棧、動態連接、方法出口等信息。每一個方法從調用至完成的過程,就對應着一個棧幀在虛擬機棧中從入棧到出棧的過程。

當被調用方法運行結束時,該方法對應的幀將被刪除,參數和局部變量所佔據的空間也隨之釋放。線程回到原方法,繼續執行。當所有的棧都清空時,程序也隨之運行結束。

我們經常說的棧內存其實就是現在講的虛擬機棧,或者說是虛擬機棧中局部變量表部分。

局部變量表存放了編譯器可知的各種基本數據類型(boolean、byte、char、short、int、float、long、double)、對象引用(reference類型,它不等同於對象本身,可能是指向對象起始位置的引用指針,也可能是指向一個代表對象的句柄或其他與此對象相關的位置,引用所指向的對象保存在堆中(引用可能爲Null,即不指向任何對象))和returnAddress類型(指向了一條字節碼指令的地址)。

其中64位長度的long和double類型的數據會佔用2個局部變量空間(Slot),其餘數據類型只佔用1個。局部變量表所需要的內存空間在編譯時期完成分配。當進入一個方法時,這個方法需要在幀中分配多大的局部變量空間是完全確定的,在方法運行期間不會改變局部變量表的大小。

異常有兩種

1,線程請求的棧深度大於虛擬機所允許的深度將拋出StackOverflowError異常 (遞歸調用)

2,如果虛擬機可以動態擴展,如果擴展時已經無法申請到足夠的內存就會拋出OutOfMemeoryError異常。

本地方法棧

本地方法棧(Native Method Stack)與虛擬機棧所發揮的作用是非常相似的。他們之間的區別就是Java虛擬機棧是位虛擬機執行Java方法(也就是字節碼)服務,而本地方法棧爲位虛擬機使用到的Native方法服務。

其實虛擬機規範中對本地方發棧中方法所使用的語言、使用方式以及數據結構都沒有強制規定,因此具體的虛擬機可以自由地實現它。甚至在有的虛擬機(如Sun HotSpot虛擬機)直接就把本地方法棧和虛擬機棧合二爲一。與虛擬機棧一樣,本地方法棧區域也會拋出StackOverflowError和OutOfMemory異常。

Java堆

對於大多數應用來說,Java堆(Java Heap)是Java虛擬機管理的內存中最大的一塊。Java堆是被所有線程共享的一塊數據區域,在虛擬機啓動時創建,這一內存區域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這裏分配內存。但是隨着JIT編譯器的發展與逃逸分析技術逐漸成熟,棧上分配、標量替換優化技術將會導致一些微妙的變化發生,所有的對象都分配在堆上也逐漸變得不是那麼“絕對”

堆中可細分爲新生代和老年代,在細分可以分爲Eden空間、Form Survivor空間、to Survivor空間。

Java堆是垃圾收集器管理的主要區域,因此很多時候也被稱爲“GC堆”。

根據Java虛擬機規範規定,Java堆可以處於物理上不連續的內存中,即只要邏輯上是連續的即可,就像我們的磁盤空間一樣。在實現時,可以固定大小也是可擴展的。主流的虛擬機都是按照可擴展來實現的(通過-Xmx和-Xms來控制)。如果在堆中沒有內存可分配,並且堆也無法繼續擴展時,將會拋出OutOfMemortError異常。

Java的普通對象存活在堆中,與棧不同,堆的空間不會隨着方法調用結束而清空。因此,在某個方法中創建的對象,可以在方法調用結束之後,繼續存在堆中。這帶來的一個問題是,如果我們不斷的創建新的對象,內存控件將會最終消耗殆盡。

方法區

方法區(Method Area)與Java堆一樣,是各個線程共享的內存區域,它用於存儲已經被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯之後的代碼等數據。雖然Java虛擬機將其描述爲堆的一個邏輯部分,但它卻有一個別名叫做Non-Heap(非堆)。目的是與Java堆區分開來。(以前很多人把方法區稱爲永久代,現在JDK1.8中已經用元數據區域取代了永久代)。

運行時常量池

運行時常量池(Runtime Constant Pool)是方法區的一部分。Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息就是常量池。用於存放編譯期生成的各種字面量和符號引用,這部分內容將在類加載後進入到方法區的運行時常量池中存放。並非預置入Class文件中常量池的內容才進入方法運行時常量池,運行期間也可能將新的常量放入池中,這種特性被開發人員利用得比較多的便是String類的intern()方法。

 

學習於 https://www.cnblogs.com/blueskyli/p/8630934.html

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章