JVM-概述和記憶體區域

JVM的優勢

Java的跨平台性

一次編譯,到處運行

JVM跨語言

舉個例子

將groovy編譯之後的class文件用jvm運行

  1. 先配置好groovy環境

  2. 新建HelloWorld.groovy

    class HelloWorld {
       static main(args) {
          println "hello groovy...";
       }
    }
    
  3. 將其編譯成class文件

    groovyc HelloWorld.groovy
    

  4. 用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

  1. 方法區和堆區是所有執行緒共享的記憶體區域;

  2. Java棧又叫做jvm虛擬機棧。

  3. 執行引擎等同於翻譯class文件的語言翻譯器。

  4. 方法區(永久代)在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

img

JDK7

img

注意:

jdk1.8及之後: 無永久代,類型資訊、欄位、方法、常量保存在本地記憶體的元空間。

但字元串常量池、靜態變數仍留在堆空間。

除此之外,元空間(或稱方法區),不再使用虛擬機記憶體,而是使用本地記憶體。

JDK8

img

參考資料

JVM_01 簡介 – 傲嬌的大王 – 部落格園

(3條消息)JVM學習筆記(三)——記憶體管理和垃圾回收_走向架構師之路-CSDN部落格

(3條消息)Java虛擬機(JVM)面試題(2020最新版)_ThinkWon的部落格-CSDN部落格

(4條消息)Java方法區、棧及堆_蝸牛-CSDN部落格

Jvm垃圾回收器(終結篇) – 不二塵 – 部落格園

24個Jvm面試題總結及答案_ITPUB部落格

Tags: