1. 程式人生 > 其它 >淺談Java中的JVM

淺談Java中的JVM

淺談Java中的JVM

Java

Java之所以能夠如此流行並且經久不衰,其一次編寫,到處執行的特性起到很重要的作用,引出今天的第一個問題。

一、Java為什麼能夠跨平臺執行?

因為Java程式編譯之後會生成.class檔案,這種檔案是不能夠被硬體系統直接執行的程式碼,而是一種中間碼——位元組碼。如果硬體系統想要執行這種程式碼,就需要JVM的幫助。
JVM(Java Virtual Machine),中文一般叫做Java虛擬機器,在不同的硬體平臺上安裝有不同版本的Java虛擬機器,在Java程式編譯後,由JVM將位元組碼翻譯成對應平臺能夠執行的程式碼。因此對於Java程式設計者來說,根本不需要考慮硬體平臺是什麼,可以更加專注在程式本身的邏輯。

Java的跨平臺就是通過在不同的平臺安裝相應的JVM來實現的


Java的工作過程

二、如何安裝JVM

JVM是不能夠單獨安裝的,他是嵌入在JDK或JRE中的。

名詞解釋
JDK:Java Development Kit(Java開發工具包) JDK作為開發工具,一般都是包含JRE的。
JRE:Java Runtime Environment(Java執行時的環境) JRE本質上是通過JVM虛擬機器來執行
JVM:Java Vitual Machine(Java虛擬機器)Java的跨平臺就是通過在不同的平臺安裝相應的JVM來實現的

1. JDK JRE JVM的關係:


JDK,JRE,JVM的關係

Java虛擬機器有自己完善的硬體架構,如處理器、堆疊等,還具有相應的指令系統。

三、JVM的記憶體結構和記憶體分配

1. JVM 記憶體模型


JDK1.8之前


JDK1.8

1.方法區是靜態分配的,編譯器將變數繫結在某個儲存位置上,而且這些繫結是不會在執行時改變。常量池,原始碼中的命名常量String常量static變數儲存在方法區。

2.Java stack 是一個邏輯概念,特點是後進先出。一個棧的空間可能是連續的,也可能是不連續的。最典型的stack應用是方法的呃呼叫,Java虛擬機器每呼叫一次方法就建立一個方法幀(frame),退出該方法則對應的方法幀被彈出(pop)

。棧中儲存的資料也是執行時確定的。所謂的虛擬機器棧就是常說的棧。
本地方法棧和虛擬機器棧所發揮的作用非常相似,區別是:虛擬機器棧為虛擬機器執行Java方法(也就是位元組碼)服務,而本地方法棧則是為虛擬機器使用到的Native方法服務。在HotSpot虛擬機器中的Java虛擬機器棧合二為一

3.Java堆分配(heep allocation)意味著以隨意的順序,在執行時進行儲存空間分配和收回的記憶體管理模型。堆中儲存的資料常常是大小、數量和生命週期在編譯時無法確定的。Java物件的記憶體總是在heap中分配。我們每天都在寫程式碼,每天都在使用JVM的記憶體。

4.程式計數器:位元組碼直譯器通過改變程式計數器來依次讀取指令,從而實現程式碼的流程控制,如:順序執行、選擇、迴圈、異常處理。在多執行緒的情況下,程式計數器用於記錄當前執行緒執行的位置,從而當執行緒切換回來的時候能夠知道該執行緒上次執行到哪個位置。

5.JDK1.8的時候,方法區(HotSpot的永久代)被徹底移除了,(JDK1.7就已經開始了),取而代之的是元空間,元空間使用的是直接記憶體

2. Java的記憶體分配

1.基礎資料型別直接在棧空間分配
2.方法的形式引數,直接在棧空間分配,當方法呼叫完成後從棧空間釋放
3.引用資料型別,需要用new來建立,既在棧空間分配一個地址空間,又在堆空間分配物件的類變數
4.方法的引用引數,在棧空間分配一個地址空間,並指向堆空間的物件區,當方法呼叫完後從棧空間回收
5.區域性變數new出來時,在棧空間堆空間中分配空間,當局部變數生命週期結束之後,棧空間會立刻被回收,堆空間區域等待GC回收
6.方法呼叫時傳入的實際引數,先在棧空間分配,在方法呼叫完成後從棧空間釋放
7.字串常量在DATA區域分配this在對空間分配
8.陣列既在棧空間分配陣列名稱,又在堆空間分配陣列實際的大小

3. JVM垃圾回收機制和常見演算法

理論上來講,Sun公司只定義了垃圾回收機制規則而不侷限於其實現演算法,因此不同廠商生產的虛擬機器採用的演算法也不盡相同。
GC(Garbage Collector)在回收物件前首先必須發現那些無用的物件,如何去發現定位這些無用的物件?常用的搜尋演算法如下:

3-1. 引用計數器演算法(廢棄)

引用計數器演算法是給每個物件設定一個計數器,當有地方引用這個物件的時候,計數器+1,當引用失效的時候,計數器-1,當計數器為0的時候,JVM就認為物件不再被使用,是垃圾了。引用計數器實現簡單,效率高但是不能解決迴圈引用問題(A物件 引用 B物件,B物件 又引用 A物件,但是A,B物件已不被任何其他物件引用),同時每次計數器的增加和減少都帶來了很多額外的開銷,所以在JDK1.1之後,這個演算法已經不在使用了

3-2. 根搜尋演算法(使用)

根搜尋演算法是通過一些GC Roots物件作為起點,從這些節點開始往下搜尋,搜尋通過的路徑成為引用鏈Reference Chain,當一個物件沒有被GC Roots的引用鏈連線的時候,說明這個物件是不可用的

GC Roots 物件包括

a).虛擬機器棧(棧幀中的本地變量表)中的引用的物件
b).方法區域中的類靜態屬性引用的物件
c).方法區域中常量引用的物件
d).本地方法棧中JNI(Native方法)的引用的物件

通過上面的演算法搜尋到無用物件之後,就是回收過程,回收演算法如下:

  1. 標記-清除演算法(Mark-Sweep)(DVM使用的演算法)

標記-清除演算法包括兩個階段:標記清除在標記階段,確定所有要回收的物件,並做標記清除階段緊隨標記階段,將標記階段確定不可用的物件清除。標記-清除演算法是基礎的收集演算法,標記和清除階段的效率不高而且清除後會產生大量的不連續空間,這樣當程式需要分配大記憶體物件時,可能無法找到足夠的連續空間

  1. 複製演算法(Copying)

複製演算法是把記憶體分成大小相等的兩塊,每次使用其中一塊,當垃圾回收的時候,把存活的物件複製另一塊上,然後把這塊記憶體整個清理掉。複製演算法實現簡單,執行效率高,但是由於每次只能使用其中的一半,造成記憶體的利用率不高。現在的JVM用複製方法收集新生代,由於新生代中大部分物件(98%)都是朝生夕死的,所以兩塊記憶體的比例不是1:1(大概8:1)

  1. 標記-整理演算法(Mark-Compact)

標記-整理演算法和標記-清除演算法一樣,但是標記-整理演算法不是把存活物件複製到到另一塊記憶體,而是把存貨物件往記憶體的一端移動,然後直接回收邊界以外的記憶體。標記-整理演算法提供了記憶體的利用率,並且它適合在收集物件存活時間較長的老年代

  1. 分代收集(Generational Collection)

分代收集是根據物件的存活時間把記憶體分為新生代和老生代,根據各個物件的存活特點,每個帶採用不同的垃圾回收演算法。新生代採用複製演算法,老年代採用標記-整理演算法。垃圾演算法的實現涉及大量的程式細節,而且不同的虛擬機器平臺實現的方法也各不相同。

四、JVM的元件

JVM是Java的虛擬機器,其大致可以包括

類載入器:用於載入類
垃圾回收器:回收無用的物件
方法區:也稱為永久代非堆,它用於儲存虛擬機器載入的類資訊、常量、靜態變數、是各個執行緒共享的記憶體區域
Java堆:java虛擬機器所管理的記憶體中最大的一個塊記憶體區域,也是被各個執行緒共享的記憶體區域,在JVM啟動時建立。該記憶體區域存放了物件例項及陣列(所有new出來的物件)。由於現在收集器都是採用分代收集演算法,堆被劃分為新生代和老生代。新生代主要儲存新建立的物件和尚未進入老年代的物件。老年代儲存經過多次新生代GC(Minor GC)仍然存活的物件。
Java棧:描述的是Java方法執行的記憶體模型:每個方法被執行的時候都會建立一個棧幀,用於儲存區域性變量表(包括引數)、操作站、方法出口等資訊。每個方法被呼叫到執行完的過程,就對應著一個棧幀在虛擬機器棧中從入棧到出棧的過程。生命週期與執行緒相同,是執行緒私有的。
本地方法棧:與虛擬機器棧基本類似,區別在於虛擬機器棧為虛擬機器執行的Java方法服務,而本地方法棧則是為Native方法服務
執行引擎
本地方法介面
指令暫存器


JVM結構圖