JVM的優勢
Java的跨平臺性
一次編譯,到處執行
JVM跨語言
舉個例子
將groovy編譯之後的class檔案用jvm執行
-
先配置好groovy環境
-
新建HelloWorld.groovy
class HelloWorld { static main(args) { println "hello groovy..."; } }
-
將其編譯成class檔案
groovyc HelloWorld.groovy
-
用java命令執行groovy編譯出來的HelloWorld.class檔案
(注:全域性搜尋groovy-all-xxxx.jar的jar包,將其路徑作為classpath後的引數)java -classpath "E:\codingEnvironment\IntelliJ IDEA 2019.1.3\lib\groovy-all-2.4.15.jar;." HelloWorld
JVM整體結構
HotSpot VM
-
方法區和堆區是所有執行緒共享的記憶體區域;
-
Java棧又叫做jvm虛擬機器棧。
-
執行引擎等同於翻譯class檔案的語言翻譯器。
-
方法區(永久代)在jdk8中又叫做元空間
Metaspace
- 方法區用於儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器(JIT編譯器,英文寫作Just-In-Time Compiler)編譯後的程式碼等資料。
- 雖然Java虛擬機器規範把方法區描述為堆的一個邏輯部分,但是它卻有一個別名叫做 Non-Heap(非堆),目的應該是與 Java 堆區分開來。
執行時資料區
概述
堆記憶體:儲存所有引用資料的真實資訊;
棧記憶體:基本型別、運算、指向堆記憶體的指標;
方法區:所以定義的方法的資訊都儲存方法區中,屬於共享區;
程式計數器:是一個非常小的記憶體空間,用來保證程式依次執行;
本地方法棧:每一次執行遞迴方法的時候,都會將上一個方法入棧;
方法區(Method Area)
1. 什麼是方法區(Method Area)?
方法區(Method Area)與Java堆一樣,是各個執行緒共享的記憶體區域。
2.方法區(Method Area)儲存什麼?
它儲存已被Java虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等
域資訊(成員變數)和方法資訊可以看成在型別資訊內
2.1 類資訊
對每個載入的型別(類class、介面interface、列舉enum、註解annotation),JVM必須在方法區中儲存以下型別資訊:
- 這個型別的完整有效名稱(全名=包名.類名)
- 這個型別直接父類的完整有效名稱(
java.lang.Object
除外,其他型別若沒有宣告父類,預設父類是Object) - 這個型別的修飾符(
public、abstract、final
的某個子集) - 這個型別直接介面的一個有序列表
除此之外還方法區(Method Area)儲存類資訊還有 - 型別的常量池( constant pool)
- 域(Field)資訊
- 方法(Method)資訊
- 除了常量外的所有靜態(static)變數
方法區(Method Area)儲存類資訊請參考:參考部落格
2.2 常量
- static final修飾的成員變數都儲存於 方法區(Method Area)中
2.3 靜態變數
- 靜態變數又稱為類變數,類中被static修飾的成員變數都是靜態變數(類變數)
- 靜態變數之所以又稱為類變數,是因為靜態變數和類關聯在一起,隨著類的載入而存在於方法區(而不是堆中)
- 八種基本資料型別(byte、short、int、long、float、double、char、boolean)的靜態變數會在方法區開闢空間,並將對應的值儲存在方法方法區,對於引用型別的靜態變數如果未用
new
關鍵字為引用型別的靜態變數分配物件(如:static Object obj;
)那麼物件的引用obj會儲存在方法區中,併為其指定預設值null
;若,對於引用型別的靜態變數如果用new
關鍵字為引用型別的靜態變數分配物件(如:static Person person = new Person();
),那麼物件的引用person 會儲存在方法區中,並且該物件在堆中的地址也會儲存在方法區中(注意此時靜態變數只儲存了物件的堆地址,而物件本身仍在堆記憶體中);這個過程還涉及到靜態變數初始化問題,可以參考部落格:靜態變數初始化相關
2.4 方法(Method)
- 程式執行時會載入類編譯生成的位元組碼,這個過程中靜態變數(類變數)和靜態方法及普通方法對應的位元組碼載入到方法區。
- 但是!!!方法區中沒有例項變數,這是因為,類載入先於對應類物件的產生,而例項變數是和物件關聯在一起的,沒有物件就不存在例項變數,類載入時沒有物件,所以方法區中沒有例項變數
- 靜態變數(類變數)和靜態方法及普通方法在方法區(Method Area)儲存方式是有區別的
棧(Stack)
棧(Stack):執行緒私有的記憶體區域
- 每個方法(Method)執行時,都會建立一個棧幀,用於儲存區域性變數表、運算元棧、動態連結、方法出口資訊等
- 棧中所儲存的變數和引用都是區域性的(即:定義在方法體中的變數或者引用),區域性變數和引用都在棧中(包括final的區域性變數)
- 八種基本資料型別(byte、short、int、long、float、double、char、boolean)的區域性變數(定義在方法體中的基本資料型別的變數)在棧中儲存的是它們對應的值
- 棧中還儲存區域性的物件的引用(定義在方法體中的引用型別的變數),物件的引用並不是物件本身,而是物件在堆中的地址,換句話說,區域性的物件的引用所指物件在堆中的地址在儲存在了棧中。當然,如果物件的引用沒有指向具體的物件,物件的引用則是
null
Java堆(Java Heap)
Java堆(Java Heap) :被所有執行緒共享的一塊記憶體區域,在虛擬機器啟動時建立。Java堆(Java Heap)唯一目的就是存放物件例項。所有的物件例項及陣列都要在Java堆(Java Heap)上分配記憶體空間。
- 由關鍵字new產生的所有物件都儲存於Java堆(Java Heap)
- !!! 例項變數(非static修飾的成員變數)和物件關聯在一起,所以例項變數也在堆中
- java陣列也在堆中開闢記憶體空間
棧、堆和方法區的關係
Java程式碼大致執行流程
java源程式--編譯javac-->位元組碼檔案.class-->類裝載子系統生成反射類(存入方法區)--->執行時資料區(五大塊兒)--->執行引擎-->解釋執行+編譯執行(JIT)-->作業系統(Win,Linux,Mac JVM)
作用
將高階語言轉化為機器能聽得懂的機器指令
Hotspot中方法區的變動
關於方法區的結構,在過去的版本jdk1.6/1.7/1.8當中均有變動,故在此提前宣告:
- jdk1.6及之前:有永久代(permanent generation) ,靜態變數、字串常量池存放在 永久代上。
- jdk1.7:有永久代,但已經逐步“去永久代”,字串常量池、靜態變數移除,儲存在堆中。注意:
- jdk1.8及之後: 無永久代,型別資訊、欄位、方法、常量儲存在本地記憶體的元空間,但字串常量池、靜態變數仍留在堆空間.
JDK6
JDK7
注意:
jdk1.8及之後: 無永久代,型別資訊、欄位、方法、常量儲存在本地記憶體的元空間。
但字串常量池、靜態變數仍留在堆空間。
除此之外,元空間(或稱方法區),不再使用虛擬機器記憶體,而是使用本地記憶體。
JDK8
參考資料
(3條訊息)JVM學習筆記(三)------記憶體管理和垃圾回收_走向架構師之路-CSDN部落格