jvm記憶體模型介紹
什麼是JVM?
所謂JVM就是JAVA虛擬機器(Java Virtual Machine)。這也正是Java牛逼的地方所在,眾所周知,Java的特點就是“一次編譯,到處執行”。這就是JVM做到的,JVM就是一臺虛擬的計算機,把具體的機器指令遮蔽起來,用自己獨有的一套東西。開發者編寫的程式經過編譯器生成Java虛擬機器上執行的目的碼(位元組碼),就可以無視平臺,帶來的弊端也顯而易見,Java虛擬機器在執行位元組碼時,也需要把位元組碼解釋成具體平臺上的機器指令執行。舉個例子:我們吃飯,西餐用刀叉,吃中餐用筷子,吃漢堡直接用手,我們需要針對吃不同的東西選擇不同的方式,這就相當於我寫Windows程式,或者寫Mac程式,亦或者寫Android程式,寫不同的程式我要用不同的語言和編輯器。JVM是什麼呢?就相當於我吃飯工具的一個盒子,裡面有各種各樣的工具,我不需要考慮我吃的是什麼,吃就完了,反正有盒子替我考慮。
JRE/JDK/JVM是什麼關係?
JRE(JavaRuntimeEnvironment,Java執行環境),也就是Java平臺。所有的Java 程式都要在JRE下才能執行。普通使用者只需要執行已開發好的java程式,安裝JRE即可。
JDK(Java Development Kit)是程式開發者用來來編譯、除錯java程式用的開發工具包。JDK的工具也是Java程式,也需要JRE才能執行。為了保持JDK的獨立性和完整性,在JDK的安裝過程中,JRE也是 安裝的一部分。所以,在JDK的安裝目錄下有一個名為jre的目錄,用於存放JRE檔案。
JVM(JavaVirtualMachine,Java虛擬機器)是JRE的一部分。它是一個虛構出來的計算機,是通過在實際的計算機上模擬模擬各種計算機功能來實現的。JVM有自己完善的硬體架構,如處理器、堆疊、暫存器等,還具有相應的指令系統。Java語言最重要的特點就是跨平臺執行。使用JVM就是為了支援與作業系統無關,實現跨平臺。
JVM原理
JVM是java的核心和基礎,在java編譯器和os平臺之間的虛擬處理器。它是一種利用軟體方法實現的抽象的計算機基於下層的作業系統和硬體平臺,可以在上面執行java的位元組碼程式。
JVM 記憶體模型圖
java編譯器只要面向JVM,生成JVM能理解的程式碼或位元組碼檔案。Java原始檔經編譯成位元組碼程式,通過JVM將每一條指令翻譯成不同平臺機器碼,通過特定平臺執行。
JVM記憶體
JVM 將記憶體區域劃分為 Method Area(Non-Heap)(方法區) ,Heap(堆) , Program Counter Register(程式計數器) , VM Stack(虛擬機器棧,也有翻譯成JAVA 方法棧的),Native Method Stack ( 本地方法棧 ),其中Method Area 和 Heap 是執行緒共享
首先我們熟悉一下一個一般性的 Java 程式的工作過程。一個 Java 源程式檔案,會被編譯為位元組碼檔案(以 class 為副檔名),每個java程式都需要執行在自己的JVM上,然後告知 JVM 程式的執行入口,再被 JVM 通過位元組碼直譯器載入執行。那麼程式開始執行後,都是如何涉及到各記憶體區域的呢?
概括地說來,JVM初始執行的時候都會分配好 Method Area(方法區) 和Heap(堆) ,而JVM 每遇到一個執行緒,就為其分配一個 Program Counter Register(程式計數器) , VM Stack(虛擬機器棧)和Native Method Stack (本地方法棧), 當執行緒終止時,三者(虛擬機器棧,本地方法棧和程式計數器)所佔用的記憶體空間也會被釋放掉。這也是為什麼我把記憶體區域分為執行緒共享和非執行緒共享的原因,非執行緒共享的那三個區域的生命週期與所屬執行緒相同,而執行緒共享的區域與JAVA程式執行的生命週期相同,所以這也是系統垃圾回收的場所只發生線上程共享的區域(實際上對大部分虛擬機器來說知發生在Heap上)的原因。
堆(Heap)是JVM用來儲存物件例項以及陣列值的區域,可以認為Java中所有通過new建立的物件的記憶體都在此分配,Heap中的物件的記憶體需要等待GC進行回收。
- 堆是JVM中所有執行緒共享的,因此在其上進行物件記憶體的分配均需要進行加鎖,這也導致了new物件的開銷是比較大的
- Sun Hotspot JVM為了提升物件記憶體分配的效率,對於所建立的執行緒都會分配一塊獨立的空間TLAB(Thread Local
Allocation
Buffer),其大小由JVM根據執行的情況計算而得,在TLAB上分配物件時不需要加鎖,因此JVM在給執行緒的物件分配記憶體時會盡量的在TLAB上分配,在這種情況下JVM中分配物件記憶體的效能和C基本是一樣高效的,但如果物件過大的話則仍然是直接使用堆空間分配 - TLAB僅作用於新生代的Eden Space,因此在編寫Java程式時,通常多個小的物件比大的物件分配起來更加高效。
- 所有新建立的Object 都將會儲存在新生代Yong Generation中。如果Young
Generation的資料在一次或多次GC後存活下來,那麼將被轉移到OldGeneration。新的Object總是建立在Eden Space。
堆我並不是很理解,大佬們幫助理解一下,十分感謝。
方法區域(Method Area)存放了所載入的類的資訊(名稱、修飾符等)、類中的靜態變數、類中定義為final型別的常量、類中的Field資訊、類中的方法資訊,當開發人員在程式中通過Class物件中的getName、isInterface等方法來獲取資訊時,這些資料都來源於方法區域,同時方法區域也是全域性共享的,在一定的條件下它也會被GC,當方法區域需要使用的記憶體超過其允許的大小時,會丟擲OutOfMemory的錯誤資訊
本地方法堆疊(Native Method Stacks)存放每個native方法的呼叫狀態。
程式計數器(Program Counter Register)是一個比較小的記憶體區域,用於指示當前執行緒所執行的位元組碼執行到了第幾行。
虛擬機器棧(VM Stack)描述的是Java方法執行的記憶體模型,用於儲存區域性變數,運算元棧,動態連結,方法出口等資訊。
JVM垃圾回收
GC (Garbage Collection)的基本原理:將記憶體中不再被使用的物件進行回收,GC中用於回收的方法稱為收集器,由於GC需要消耗一些資源和時間,Java在對物件的生命週期特徵進行分析後,按照新生代、舊生代的方式來對物件進行收集,以儘可能的縮短GC對應用造成的暫停
- 對新生代的物件的收集稱為minor GC;
- 對舊生代的物件的收集稱為Full GC;
- 程式中主動呼叫System.gc()強制執行的GC為Full GC。
- 不同的物件引用型別, GC會採用不同的方法進行回收,JVM物件的引用分為了四種類型:
(1)強引用:預設情況下,物件採用的均為強引用(這個物件的例項沒有其他物件引用,GC時才會被回收)
(2)軟引用:軟引用是Java中提供的一種比較適合於快取場景的應用(只有在記憶體不夠用的情況下才會被GC)
(3)弱引用:在GC時一定會被GC回收
(4)虛引用:由於虛引用只是用來得知物件是否被GC