JVM-概述和記憶體區域
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部落格
(3條消息)Java虛擬機(JVM)面試題(2020最新版)_ThinkWon的部落格-CSDN部落格