文章目錄了解JVM(Java虛擬機)首先我們必須了解VM(虛擬機)是什么。 所謂虛擬機(Virtual Machine),就是一臺虛擬的計算機。它是一款軟件,用來執行一系列虛擬計算機指令。大體上,虛擬機可以分為系統虛擬機和程序虛擬機。 我們經常見到的 VMware 就屬于系統虛擬機,它是完全對物理計算機的仿真,提供了一個可運行完整操作系統的軟件平臺。 程序虛擬機典型的代表就是 Java 虛擬機了,它專門為執行某個單個計算機程序而設計。在 Java 虛擬機中執行的指令我們稱為 Java 字節碼指令。無論是系統虛擬機還是程序虛擬機,在上面運行的軟件都被限制于虛擬機提供的資源。Java 虛擬機是一種執行 Java 字節碼文件的虛擬計算機,它擁有獨立的運行機制。 Java 技術的核心就是 Java 虛擬機,因為所有的 Java 程序都運行在 Java 虛擬機內部。 JVM概述JVM就是Java虛擬機,虛擬機就是一臺虛擬的計算機,是一款軟件。Java 虛擬機就是二進制字節碼的運行環境,負責裝載字節碼到其內部,解釋或編譯為對應平臺上的機器碼指令執行,每一條 Java 指令,Java 虛擬機中都有詳細定義,如怎么取操作數,怎么處理操作數,處理結果放在哪兒等。JVM是運行在操作系統上的,不與硬件直接交互。 JVM整體的四個部分JVM整體組成可以分為4個部分:
程序在執行之前先要把 Java 代碼轉換成字節碼(.class 文件),JVM 首先需要把字節碼通過一定的類加載器(Class Loader)把文件加載到內存中運行時數據區(Runtime Data Area),而字節碼文件是 JVM 的一套指令集規范,并不能直接交給底層操作系統去執行,因此需要特定的命令解析器(執行引擎(Execution Engine)) 將字節碼翻譯成底層系統指令再交由 CPU 去執行,而這個過程中需要調用其他語言的接口(本地方法接口(Native Interface)) 來實現。整個程序的功能,這就是這 4 個主要組成部分的職責與功能。 1.1 類加載器
1.1.1 類加載器過程類加載器子系統負責從文件系統或者網絡中加載 class 文件, class 文件在文件開頭有特定的文件標識(字節碼文件都以 CA FE BA BE 標識開頭)。classLoader 只負責 class 文件的加載,至于它是否可以運行,則由執行引擎決定。加載的類信息存放于一塊稱為方法區的內存空間。除了類的信息外,方法區中還會存放運行時常量池信息,可能還包括字符串字面量和數字常量(這部分常量信息是 class 文件中常量池部分的內存映射)。 類加載的過程 1.加載過程是把class文件(字節碼文件)加載到內存中(I/O讀寫)。類加載器把文件加載到內存中,會為每個類創建一個Class類的對象,調用Class類中的方法獲取類的相關信息。 2.驗證是檢驗加載的類是否有正確的內部結構并和其他類協調一致。 3.準備階段為類的靜態屬性分配內存,并設置默認初始值(不包含final修飾的static常量),也不會給實例變量初始化。 4.解析是將二進制數據中的符號引用替換成直接引用(符號引用是用一組符號描述所引用的目標;直接引用是指向目標的指針)。 5.類初始化 ? 5.1 什么時候初始化類 ? 1 )創建類的實例,也就是 new 一個對象 ? 5.2 類的初始化順序: ? 父類static --> 子類static --> 父類構造方法 --> 子類構造方法 1.1.2類加載器的分類1.1.2.1啟動類加載器(引導類加載器)是由c/c++實現,用來加載Java的核心類庫 1.1.2.2擴展類加載器由Java實現,派生于 ClassLoader 類。上層類加載器是引導類加載器(啟動類加載器),加載底層類庫 1.1.2.3應用程序類加載器Java實現,派生于 ClassLoader 類。上層類加載器是擴展類加載器,加載自定義類。 1.1.3 雙親委派機制加載類時,向上委派,交給最上層的加載器啟動類加載器加載核心類庫,加載不到就用擴展類加載器加載底層類庫,加載不到就用應用程序類加載器加載自定義類 雙親委派機制的優點
1.1.4沙箱安全機制作用是防止惡意代碼污染 Java 源代碼 如果一個類在引導類加載器那里就加載到了,先找到先使用,所以就使用引導類加載器里面的類,后面的一概不能使用,這就保證了不被惡意代碼污染。 1.1.5 類的主動使用和被動使用JVM 規定,每個類或者接口被首次主動使用時才對其進行初始化,有主動使用,自然就有被動使用。
被動使用:其實除了上面的幾種主動使用其余就是被動使用了 注意:主動使用和被動使用的區別在于類是否會被初始化.
1.2 運行時數據區JVM 的運行時數據區,不同虛擬機實現可能略微有所不同,但都會遵從 Java 虛擬機規范,Java 8 虛擬機規范規定,Java 虛擬機所管理的內存將會包括程序計數器、Java虛擬機棧、本地方法棧、Java堆、方法區。 1.2.1 程序計數器(Program Counter Register)JVM 中的程序計數寄存器中的 Register 命名源于CPU 的寄存器,寄存器存儲指令相關的現場信息。這里,并非是廣義上所指的物理寄存器,或許將其翻譯為 PC 計數器(或指令計數器)會更加貼切(也稱為程序鉤子)。JVM 中的PC 寄存器是對物理 PC 寄存器的一種抽象模擬。
那么為什么使用程序計數器記錄當前線程的執行地址呢?我們都知道CPU需要不停的切換各個線程,這時候從其他線程切換到這個線程時,根據程序計數器就知道該從哪里開始執行了。JVM 的字節碼解釋器就需要通過改變程序計數器的值來明確下一條應該執行什么樣的字節碼指令,由于這個原因,必須為每個線程分配一個程序計數器,這樣各個線程就可以互不干擾。 1.2.2 Java 虛擬機棧(Java Virtual Machine Stacks)首先我們需要學會區分棧和堆: 棧是運行時的單位,而堆時存儲的單位。
1.2.2.1 Java虛擬機棧概述每個線程在創建時都會創建一個虛擬機棧,其內部保存一個個棧幀,對應著一次方法的調用。Java 虛擬機棧是線程私有的。生命周期和線程一致。棧中的數據都以棧幀為單位存儲。棧幀是一個內存區塊,是一個數據集,維系著方法執行過程中的各種數據信息。 作用:主要負責Java程序的運行,保存方法內的局部變量,還有部分結果,還參與方法的調用和返回。 棧是一種快速有效的分配存儲方式,訪問速度僅次于程序計數器。JVM 直接對 Java 棧的操作只有兩個:
注意:對于棧來說不存在垃圾回收問題。 1.2.2.2棧的運行原理
1.2.2.3 棧幀的內部每個棧幀中都有:局部變量表、操作數棧、動態鏈接、方法返回地址、一下附加信息。 1.2.2.3.1局部變量表(Local Variables)局部變量表用于存放方法參數和方法內部定義的局部變量。 對于基本數據類型的變量,則直接存儲它的值,對于引用類型的變量,則存的是指向對象的引用。 1.2.2.3.2 操作數棧(Operand Stack)(或表達式棧)程序中的所有計算過程都是在借助于操作數棧來完成的。 1.2.2.3.3 動態鏈接(Dynamic Linking) (或指向運行時常量池的方法引用)因為在方法執行的過程中有可能需要用到類中的常量或方法,所以必須要有一個引用指向運行時常量池。 1.2.2.3.4 方法返回地址(Return Address)(或方法正常退出或者異常退出的定義)當一個方法執行完畢之后,要返回之前調用它的地方,因此在棧幀中必須保存一個方法返回地址。 1.2.2.3.5 一些附加信息例如和調試相關的信息,這部分信息完全取決于不同的虛擬機實現。 1.2.3 本地方法棧
內存溢出方面也是相同的。
1.2.4 堆內存堆內存概述:
我們在之后在細講堆內存的區域劃分及垃圾回收機制 1.2.5方法區方法區,是一個被線程共享的內存區域。其中主要存儲加載的類字節碼、class/method/field 等元數據、static final 常量、static 變量、編譯器編譯后的代碼等數據。另外,方法區包含了一個特殊的區域“運行時常量池”。 結語這次就先寫這些,文中如果存在不對的地方,歡迎各位讀者批評指正。我會在今后更新本地方法接口、執行引擎和垃圾回收機制等相關內容,如果大家感興趣,可以關注博主,我們一起交流學習。
|
|