【技術積累】Java中的JVM【一】

2023-06-07 18:00:54

什麼是JVM

JVM英文全稱為Java Virtual Machine,中文意為Java虛擬機器器。JVM是一種能夠執行Java語言編寫的程式的虛擬機器器器,它首次作為Java語言的一部分,後來又被移植到了許多平臺上。

JVM可以執行Java位元組碼,即Java原始碼經過編譯後生成的二進位制中間程式碼。JVM根據位元組碼指令來執行程式,這就使得Java程式跨平臺,在不同的作業系統上執行時不需要修改原始碼。JVM本身也是跨平臺的,因為JVM實際上就是使用底層機器程式碼實現的軟體層,因此只需安裝相應平臺的JVM即可。

JVM的主要元件包括:

  1. 類載入器:負責將.class二進位制檔案轉換為在執行時可以被JVM識別的類物件。

  2. 執行時資料區:包括方法區、虛擬機器器棧、本地方法棧、堆等。Java程式在執行時需要佔用這些區域中的記憶體。

  3. 執行引擎:負責執行位元組碼指令。

  4. 本地方法介面:使得Java程式可以呼叫本地方法,也就是使用C或C++重新編寫的底層程式。

JVM為Java語言提供了許多高階特性,這些特性包括垃圾回收、例外處理、執行緒控制和動態程式碼載入。因此,JVM是許多Java開發者不可缺少的工具,也是Java語言的核心元件之一。

JVM是如何執行Java程式的?

JVM(Java Virtual Machine)是Java程式的執行環境,負責將Java程式碼轉換為機器可以執行的程式碼。JVM執行Java程式的過程如下:

  1. 讀取並解析位元組碼檔案:JVM讀取.class檔案並將其解析為JVM可以理解的指令集。這些指令被儲存在方法區(Method Area)中。

  2. 載入類:JVM根據程式需要動態地載入類。當需要使用某個類時,JVM會檢查該類是否已經載入過,如果沒有則JVM會從檔案系統或網路中載入該類。

  3. 程式計數器指向當前指令:JVM中的程式計數器(Program Counter)指向當前正在執行的指令。在執行指令之前,程式計數器先指向該指令的地址。

  4. 執行指令集:JVM通過直譯器或即時編譯器來執行指令集。直譯器逐條解釋指令並執行,而即時編譯器將整段指令編譯成原生程式碼後再執行。執行指令集時,JVM還需要進行記憶體分配、垃圾回收等操作。

  5. 程式計數器指向下一條指令:執行完一條指令後,程式計數器會指向下一條指令的地址。這個過程不斷迴圈,直到程式結束或異常丟擲。

總之,JVM負責將Java程式轉換成機器可以執行的程式碼,並提供執行時環境。JVM的執行過程中,會載入類、解釋和執行指令集、記憶體分配和垃圾回收等操作。

什麼是JIT編譯器?

JIT(Just-In-Time)編譯器是一種動態編譯器,它在程式執行時將位元組碼翻譯為本地機器程式碼。在JIT編譯器中,程式碼被逐行直譯器執行,當某些程式碼被頻繁呼叫時,JIT編譯器將這些程式碼編譯成本地機器程式碼以提高執行效率。這種編譯方式可以使程式在第一次執行時速度較慢,但在後續執行時速度會顯著提高。JIT編譯器常用於Java虛擬機器器等動態語言環境中。

JVM的記憶體模型是什麼樣子的?

JVM的記憶體模型是基於Java程式執行時記憶體的管理結構,主要分為以下幾個部分:

  1. 程式計數器:每個執行緒都有一個程式計數器,用於記錄當前執行緒執行的位元組碼行號。

  2. 虛擬機器器棧:每個執行緒都有一個虛擬機器器棧,用於儲存方法執行時的區域性變數表、運算元棧、動態連線和方法返回值等資訊。

  3. 堆:JVM執行時動態分配的記憶體區域,用於儲存Java物件和陣列。

  4. 方法區:用於儲存類資訊、常數、靜態變數、即時編譯器編譯後的程式碼等資料。

  5. 本地方法棧:與虛擬機器器棧類似,用於儲存本地方法的資訊。

在JVM中,堆是唯一一個被所有執行緒共用的記憶體區域,而每個執行緒都有自己的程式計數器、虛擬機器器棧和本地方法棧。同時,方法區也是被所有執行緒所共用的。JVM的記憶體模型為Java程式提供了良好的記憶體管理機制,能夠有效地提高程式的效能和安全性。

什麼是垃圾回收器?

JVM中的垃圾回收器是一種自動記憶體管理機制,它負責回收在程式中不再被使用的物件,並將這些記憶體釋放回給作業系統。垃圾回收器通過掃描參照關係來確定哪些記憶體是「垃圾」,並將其標記為可回收。它可以大大減少程式設計師手動釋放記憶體的工作量,提高程式的安全性和可靠性,但也會在一定程度上影響程式的效能。JVM中有很多不同型別的垃圾回收器,例如標記清除垃圾回收器、複製垃圾回收器、標記整理垃圾回收器等。這些垃圾回收器有不同的實現方式和適用場景,程式設計師需要根據需求選擇合適的垃圾回收器。

下面是一個使用Java語言的程式碼範例,演示垃圾回收器如何回收無用物件。

public class GarbageCollectionExample {
  public static void main(String[] args) {
    // 建立一個物件
    Person p = new Person();
    
    // 將物件設定為null,表示不再參照該物件
    p = null;
    
    // 垃圾回收器會在這裡回收無用物件
  }
}

class Person {
  // 將在這裡新增Person類的屬性和方法
}

在上述範例中,建立了一個名為Person的類,並範例化了一個物件p。然後將p設定為null,表示不再參照該物件。當垃圾回收器檢測到該物件沒有任何參照變數參照時,就會回收該物件並釋放其佔用的記憶體。

JVM中什麼是強參照、軟參照、弱參照、虛參照?

JVM中的四種參照型別:

  1. 強參照(Strong Reference):指向物件的參照,即使記憶體不足,JVM也不會回收被強參照關聯的物件。強參照通常使用普通的Java物件參照來定義。
  1. 軟參照(Soft Reference):軟參照關聯的物件會在記憶體不足的情況下被回收,也就是說只有當JVM發現一個物件只有軟參照關聯時,才會在記憶體不足時清除該物件。軟參照通常使用SoftReference來定義。
  1. 弱參照(Weak Reference):弱參照關聯的物件在JVM進行垃圾回收時,無論記憶體是否充足都會被回收。弱參照通常使用WeakReference來定義。
  1. 虛參照(Phantom Reference):虛參照關聯的物件與弱參照關聯物件的回收時機相同,但是虛參照必須和一個參照佇列(ReferenceQueue)一起使用。虛參照通常使用PhantomReference來定義。
public class ReferenceExample {
    public static void main(String[] args) {
        // 強參照
        Object strongRef = new Object();

        // 軟參照
        SoftReference<Object> softRef = new SoftReference<>(new Object());

        // 弱參照
        WeakReference<Object> weakRef = new WeakReference<>(new Object());

        // 虛參照
        ReferenceQueue<Object> refQueue = new ReferenceQueue<>();
        PhantomReference<Object> phanRef = new PhantomReference<>(new Object(), refQueue);

        // 嘗試回收
        System.gc();

        // 判斷是否回收成功
        System.out.println(strongRef);
        System.out.println(softRef.get());
        System.out.println(weakRef.get());
        System.out.println(phanRef.get());
    }
}

JVM如何載入和查詢類檔案?

JVM載入和查詢類檔案的過程如下:

1.類載入器的工作:JVM負責從檔案系統、JAR包或網路中載入類檔案,並交給類載入器去載入。類載入器通常有四種:

  • 引導類載入器:負責載入JVM自身需要的類,如java.lang包中的類。
  • 擴充套件類載入器:負責載入JRE的擴充套件目錄中的類。
  • 應用程式類載入器:負責載入應用程式中的類。
  • 使用者自定義類載入器:負責載入使用者自定義的類。

2.類檔案的查詢:類載入器會按照類的完全限定性類名(Fully Qualified Class Name,FQCN)去查詢類檔案。類的FQCN即包名+類名。

3.類檔案的載入:當類載入器找到並讀取了類檔案後,JVM會根據類檔案的結構建立一個表示該類的Class物件,並將其存放在JVM內部的方法區中,即類的後設資料區。這個Class物件包含了該類的所有資訊,如類名、類的修飾符、欄位、方法等。

4.類的初始化:當類檔案被載入後,JVM並不立即執行靜態程式碼塊和靜態變數的預設初始化賦值,而是等待需要被初始化類的首次主動使用時才執行。

5.類的連結:為了使類能夠正確執行,JVM需要對類進行連結。連結分為三個階段:

  • 驗證(Verification):驗證類檔案是否符合JVM規範。包括檔案格式、位元組碼驗證、符號參照驗證、存取許可權驗證等。
  • 準備(Preparation):為類的靜態變數分配記憶體,並設定預設初值。
  • 解析(Resolution):將類的符號參照解析為直接參照(如類或介面的全限定名解析為該類或介面的Class物件參照)。

6.類的使用:當一個類被載入、連結和初始化後,就可以被使用了。類的使用包括建立物件、呼叫方法、存取靜態變數、執行靜態程式碼塊等。

JVM的效能調優需要注意哪些點?

JVM的效能調優需要注意以下幾個點:

  1. 堆記憶體設定:JVM的堆大小設定對系統的效能影響很大,應該根據實際情況進行適當的調整。

  2. 垃圾回收設定:垃圾回收機制對系統的效能影響也非常大,應該根據應用的負載情況和記憶體使用情況進行適當的調整。

  3. 執行緒池設定:執行緒池的大小設定影響系統的並行能力和效能,應根據實際情況進行適當調整。

  4. JVM引數設定:JVM提供了很多設定引數,如記憶體分配、GC演演算法和堆大小等,應根據實際情況進行適當調整。

  5. 應用程式碼優化:應用的程式碼質量和設計對系統的效能也有影響,應該進行優化以提高系統的效能。

  6. 系統資源設定:系統的硬體資源設定也對系統的效能有較大的影響,應根據實際情況進行適當的調整。

如何監控JVM的執行狀態?

可以通過以下幾種方式來監控JVM的執行狀態:

  1. JVisualVM:JVisualVM是一個監控和分析JVM的工具,可以檢視記憶體使用、執行緒狀態、GC情況、類載入等資訊。需要在JDK的bin目錄下執行jvisualvm命令啟動。

  2. jstat:jstat是JVM自帶的監控工具,可以檢視JVM的堆、非堆記憶體使用情況、GC情況等。通過命令列方式執行。

  3. JMX:JMX是Java Management Extension的簡稱,可以通過JMX介面來監控和管理Java應用程式。可以使用JConsole等工具檢視JVM的執行狀況。

  4. 第三方工具:如AppDynamics、New Relic等都是常用的監控工具,可以通過整合agent來監控JVM的執行狀態。

JVM如何保證Java程式的安全性?

JVM(Java虛擬機器器)可以保證Java程式的安全性,主要體現在以下幾個方面:

  1. 類載入機制:JVM在程式執行時會將需要的類載入到記憶體中,並進行校驗、解析和初始化,確保類的可靠性和正確性。如果類有被篡改或不正確的地方,JVM在載入時會直接丟擲異常,防止不安全的程式碼被執行。

  2. 記憶體管理:JVM會提供記憶體分配、回收和存取保護等功能,防止程式出現記憶體洩露或越界存取等安全問題。

  3. 安全管理器:JVM可以通過安全管理器(Security Manager)實現對Java程式進行存取控制,限制程式對系統資源的存取,防止惡意程式碼執行或對系統造成危害。

  4. 位元組碼校驗:在將Java程式碼編譯成位元組碼之前,編譯器會對程式碼進行各種檢查,檢驗其是否符合Java語言規範和安全要求。JVM在執行位元組碼時會再次對其進行驗證,確保程式碼的安全性。

  5. 安全沙箱:JVM還提供了安全沙箱(Security Sandbox)機制,將Java程式執行在一個獨立的安全環境中,能夠限制其對檔案系統、網路等系統資源的存取,從而防止惡意程式碼對系統的攻擊。