前提知識~
JDK 基本介紹
- JDK 的全稱(Java Development Kit Java 開發工具包)
- JDK = JRE + java 的開發工具[java, javac,javadoc,javap 等]
- JDK 是提供給Java 開發人員使用的,其中包含了java 的開發工具,也包括了JRE。
- 可開發、編譯、調試……
JRE 基本介紹
- JRE(Java Runtime Environment Java 運行環境)
- JRE = JVM + Java 的核心類庫[類]
- 包括Java 虛擬機(JVM Java Virtual Machine)和Java 程序所需的核心類庫等,如果只想運行開發好的.class 文件只需要JRE。也稱最小運行環境。
- 只運行
JVM是什么?
- JVM(Java Virtual Machine,? Java虛擬機)
JVM有什么用?
與其他語言不同,Java 語言并不直接將代碼編譯成與系統有關的機器碼,而是編譯成一種特定的語言規范,這種語言規范我們稱之為字節碼。無論 Java 程序要在 Windows 系統,還是 Mac OSX 系統,或是 Linux 系統,它首先都得編譯成字節碼文件,之后才能運行。?
我們編譯成字節碼之后,無論但是 Linux 系統、?Windows 系統都還是不認識。? ? ? ? ? ? ? ? ? ?
這時候 Java 虛擬機就是一個翻譯官,解析字節碼文件的內容,在 Linux 系統上翻譯成 Linux 機器碼給 Linux 系統聽,在 Windows 系統上翻譯成 Windows 機器碼給 Windows 系統聽。
實際上 Java 虛擬機運行的是字節碼文件(Class文件),并不是Java代碼
總結:?Java 虛擬機是一個字節碼翻譯器,它將字節碼文件翻譯成各個系統對應的機器碼,確保字節碼文件能在各個系統正確運行。
JVM的內存結構:
首先先說虛擬機內存結構和運行時數據區兩個說法是一個意思。
分為5 個:
?1.Java堆(Heap):
- 是內存中最大的一塊
- Java堆是被所有線程共享的一塊內存區域,在虛擬機啟動時創建
- 作用:存放對象實例,幾乎所有的對象實例都在這里分配內存。
- 有些時候小對象會直接在棧上進行分配,這種現象我們稱之為「棧上分配」
Java 堆根據對象存活時間的不同,Java 堆還被分為年輕代、老年代兩個區域;年輕代還被進一步劃分為 Eden 區、From Survivor 0、To Survivor 1 區。
當有對象需要分配時,一個對象永遠優先被分配在年輕代的 Eden 區,等到 Eden 區域內存不夠時,Java 虛擬機會啟動垃圾回收。此時 Eden 區中沒有被引用的對象的內存就會被回收,而一些存活時間較長的對象則會進入到老年代。在 JVM 中有一個名為 -XX:MaxTenuringThreshold 的參數專門用來設置晉升到老年代所需要經歷的 GC 次數,即在年輕代的對象經過了指定次數的 GC 后,將在下次 GC 時進入老年代。
Eden:S0?:S1 = 8:1:1?
2.方法區(Method Area):
- 作用:存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據
- 常量池其實是存放在方法區中的
- 方法區在不同版本的虛擬機有不同的表現形式,例如在 1.7 版本的 HotSpot 虛擬機中,方法區被稱為永久代(Permanent Space),而在 JDK 1.8 中則被稱之為 MetaSpace。
3.程序計數器(Program Counter Register):
- 是一塊較小的內存空間
- ?字節碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成。
- 每條線程都需要有一個獨立的程序計數器,各條線程之間的計數器互不影響,獨立存儲
- 如果線程正在執行的是一個Java方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址;如果正在執行的是Natvie方法,這個計數器值則為空(Undefined)
4.JVM棧(JVM Stacks):
- 與線程同時創建
- 每個方法被執行的時候都會同時創建一個棧幀(Stack Frame)用于存儲局部變量表、操作棧、動態鏈接、方法出口等信息。
- 每一個方法被調用直至執行完成的過程,就對應著一個棧幀在虛擬機棧中從入棧到出棧的過程。
- 簡單來說:執行 Java 代碼
5.本地方法棧(Native Method Stacks)?:
本地方法棧與虛擬機棧所發揮的作用是非常相似的,其區別不過是虛擬機棧為虛擬機執行Java方法(也就是字節碼)服務,而本地方法棧則是為虛擬機使用到的Native方法服務。
總結一下:一個 Java 文件就加載到內存中了,并且 Java 類信息就會存儲在我們的方法區中。如果創建對象,那么對象數據就會存放在 Java 堆中。如果調用方法,就會用到 PC 寄存器、Java 虛擬機棧、本地方法棧等結構。
字節碼:
前面我們知道了,在不同操作系統、不同硬件平臺上都可以做到不用修改代碼就可以運行,怎么實現的跨平臺?中間碼誕生了!即“字節碼”!
Java所有的指令有200個左右,一個字節(8位)可以存儲256種不同指令信息,一個這樣的字節成為字節碼。
那么,Java源代碼是如何轉成字節碼的呢?如圖:
?當字節碼通過類加載到JVM環境后,才可以執行,而執行分為三種模式:
- 解釋執行(javac編譯器):啟動快,運行慢
- JIT編譯執行:啟動慢,運行快
- JIT編譯與解釋混合執行(默認):?在啟動時先解釋執行,省去編譯時間
解釋器不需要像 JIT 編譯器一樣,將所有字節碼都轉化為機器碼,所以就減少了時間。而JIT 編譯器完成第一次編譯后,其會將字節碼對應的機器碼保存下來,下次可以直接使用。大家都知道,機器碼的運行效率肯定是高于 Java 解釋器的,所以我們會默認使用混合執行
類加載機制
程序三階段:編譯,加載,運行
JVM 虛擬機將字節碼讀取進內存,從而進行解析、運行等的這整個過程,我們叫:Java 虛擬機的類加載機制。(即第二階段)
類加載階段又可以細分為三個階段,為加載Load,連接Link,初始化Init。
- 加載?
- 連接:
- 驗證:驗證成員的安全性(更詳細的校驗,比如final是否合規、類型是否正確、靜態變量是否合理)
- 準備:將類中的靜態成員初始化為默認值(為靜態變量分配內存,設定默認值)
- 解析:將間接地址轉換為直接地址(將其在常量池中的符號引用替換成直接其在內存中的直接引用)
- 初始化
尾聲
【JVM】從三種認知角度重識JVM-CSDN博客
?JVM入門教程第7講:JVM 類加載機制 - 陳樹義 - 博客園
jvm系列(二):JVM內存結構 - 純潔的微笑 - 博客園
jvm系列(一):java類的加載機制 - 純潔的微笑 - 博客園