3 JVM內存區域

結構

說明

JVM 內存區域主要分爲線程私有區域【程序計數器、虛擬機棧、本地方法區】、線程共享區域【JAVA 堆、方法區】、直接內存。

線程私有數據區域生命週期與線程相同, 依賴用戶線程的啓動/結束 而 創建/銷燬(在 Hotspot VM 內, 每個線程都與操作系統的本地線程直接映射, 因此這部分內存區域的存/否跟隨本地線程的生/死對應)。

線程共享區域隨虛擬機的啓動/關閉而創建/銷燬。

直接內存並不是 JVM 運行時數據區的一部分, 但也會被頻繁的使用: 在 JDK 1.4 引入的 NIO 提供了基於 Channel 與 Buffer 的 IO 方式, 它可以使用 Native 函數庫直接分配堆外內存, 然後使用 DirectByteBuffer 對象作爲這塊內存的引用進行操作(詳見: Java I/O 擴展), 這樣就避免了在 Java 堆和 Native 堆中來回複製數據, 因此在一些場景中可以顯著提高性能。

程序計數器(線程私有)

一塊較小的內存區域,是當前線程所執行的字節碼的行號指示器。每條線程都要有一個獨立的程序計數器,這類內存也被稱爲“線程私有”的內存。

執行 Java 方法時,計數器記錄的是虛擬機字節碼指定地址(即當前指令的地址)。如果執行 Native 方法,則爲空。

該內存區域是 JVM 中唯一一個沒有規定 OutOfMemoryError 的區域。

虛擬機棧(線程私有)

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

棧幀(Frame)是用來存儲數據和部分過程結果的數據結構,同時也被用來處理動態鏈接(Dynamic Linking)、方法返回值和異常分派(Dispatch Exception)。棧幀隨着方法調用而創建,隨着方法結束而銷燬——無論方法正常完成還是異常完成(拋出了在方法內未被捕獲的異常)都算作方法結束。

JVM虛擬機棧

本地方法區(線程私有)

本地方法區和 Java Stack 作用類似,區別是虛擬機棧用於執行 Java 方法服務,而本地方法棧爲 Native 方法服務。

如果一個 VM 實現使用 C-linkage 模型來支持 Native 調用,那麼該棧會是一個 C 棧,但 HotSpot VM 直接將本地方法棧和虛擬機棧合二爲一。

堆(Heap-線程共享)- 運行時數據區

堆內存被線程共享,創建的對象和數組都保存在 Java 堆內存中,也是垃圾收集器進行垃圾收集的最重要的內存區域。

由於現代 VM 採用分代收集算法,因此 Java 堆從 GC 的角度還可以細分爲:新生代(Eden區、FromSurvivor區、ToSurvivor區)和老年代。

方法區/永久代(線程共享)

方法區/永久代(Permanent Generation)用於存儲被 JVM 加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。

HotSpot VM 把 GC 分代收集擴展至方法區,即使用 Java 堆的永久代來實現方法區,這樣 HotSpot 的垃圾收集器就可以像管理 Java 堆一樣管理這些內存,而不必爲方法區開發專門的內存管理器。永久代的內存回收的主要目標是針對常量池的回收類型的卸載,因此受益一般很小。

運行時常量池(Runtime Constant Pool)是方法區的一部分。Class 文件中除了有類的版本、字段、方法、接口等描述信息外,還有常量池(Constant Pool Table),用於存放編譯器生成的各種字面量和符號引用,這部分內容將在類加載後存放到方法區的運行時常量池中。

Java 虛擬機對 Class 文件的每一部分(包括常量池)的格式都有嚴格規定,每一個字節用於存儲哪種數據都必須符合規範,這樣纔會被虛擬機認可、裝載和執行。

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