十個問題弄清JVM&GC(一)

宜信技術學院發表於2020-07-24

每個java開發同學不管是日常工作中還是面試裡,都會遇到JDK、JVM和GC的問題。本文會從以下10個問題為切入點,帶著大家一起全面瞭解一下JVM的方方面面。

  1. JVM、JRE和JDK的區別和聯絡
  2. JVM是什麼?以及它的主要作用
  3. JVM的核心功能有哪些
  4. 類載入機制和過程
  5. 執行時資料區的邏輯結構
  6. JVM的記憶體模型
  7. 如何確定物件是垃圾
  8. 垃圾收集的演算法有哪些
  9. 各種問世的垃圾收集器
  10. JVM調優的引數配置

1、JVM、JRE和JDK的區別和聯絡

這個基本是步入java世界的入門級知識認知,首先我們來看一下來自java官網的一張圖:


從這張圖裡我們基本就可以看出“JRE”是執行Java語言編寫的程式所不可缺少的執行環境。有了JRE我們寫的java程式才可以執行起來被使用者所使用。

而“JDK”俗稱java開發工具包,它包括了Java執行環境JRE(Java Runtime Envirnment)以及一堆Java工具(javac/java/jdb等)和Java基礎的類庫(即Java API 包括rt.jar)。

但不管是JRE還是JDK都是以JVM為基石的。可以說JVM是java程式可以在某臺機器上得以執行的最底層的保障。

2、那麼什麼是JVM?它的主要作用又是什麼?

JVM是Java Virtual Machine(Java虛擬機器)的縮寫,它的用途簡單的說就是它能讓我們寫的java程式在不同的作業系統的不同CPU上執行。我們寫的java程式會利用開發工具(如Intellij idea)把它編譯成.class檔案,但這個class檔案是不能直接被作業系統識別執行的,需要利用jvm按jvm規範將編譯好的.class檔案轉變成機器語言,再交由作業系統提交給cpu去執行。

用一句話評價JVM的主要作用就是:JVM遮蔽了與具體作業系統平臺相關的資訊,使得Java程式只需生成在Java虛擬機器上執行的目的碼(位元組碼),就可以在多種平臺上不加修改地執行。

3、這麼牛的JVM的核心功能有哪些?

JVM中核心的功能總體有三塊:

  1. 類載入器:在JVM啟動時或者在類執行時將需要的class檔案載入到JVM中
  2. 執行引擎:負責執行class檔案,包括分配執行時資料區(如程式計數器、本地方法棧和虛擬棧)和 最終將class中的位元組碼指令轉為機器指令通過作業系統交給CPU執行
  3. 垃圾回收器:對JVM的堆記憶體進行管理,及時回收調無用的資源釋放記憶體空間

4、JVM類的載入機制和過程?

首先,我們談談開發工具編譯生成的class檔案是如何被JVM載入的。所謂的類載入機制其實就是:虛擬機器(JVM)把class檔案載入到記憶體中,然後對它進行正確性的校驗,檢查通過再進行解析和初始化,最終把class檔案變成一個記憶體中可以直接使用的java.lang.Class物件。

從一個class檔案的裝載到銷燬,它的生命週期基本可以分為以下五個階段:裝載、連結(驗證、準備和解析)、初始化、使用和解除安裝。

  1. 裝載:裝載(Load)階段總共有三項工作

    (1)通過類的全限定名獲取其定義的二進位制位元組流,需要藉助類裝載器(ClassLoader)完成;

    (2)在執行時資料區的“方法區”中分配一塊區域儲存這個類的資訊,包括類的基本資訊、常量和靜態變數等等;

    (3)在“Java堆”記憶體上生成一個該類的java.lang.Class物件,用於對外暴露使用該類的入口。

  2. 連結:連結(link)階段同樣有三項工作

    (1)驗證(Verify),驗證檔案格式、後設資料、位元組碼和符號引用,以保證被載入類的準確性;

    (2)準備(Prepare),為靜態變數分配記憶體並初始化為預設值。

    (3)解析(Resolve),解析階段是虛擬機器將常量池內的符號引用替換為直接引用的過程。解析動作主要針對類或介面、欄位、類方法、介面方法、方法型別、方法控制程式碼和呼叫限定符7類符號引用進行。

  3. 初始化:初始化(Initialize)階段所做的工作就是對類的靜態成員變數和靜態方法進行初始化賦值或呼叫。

比如上面的靜態變數age初始化之後的值變為了10。

在裝載階段的第(2),(3)步可以發現有執行時資料區,堆,方法區等名詞,那麼究竟什麼是“執行時資料區”,它有哪些結構構成?

5、什麼是JVM執行時資料區?及其邏輯結構

“執行時資料區”是JVM在執行Java程式的過程中出於記憶體管理方面的目的,在設計上把記憶體分為若干個不同的區域。這些區域有著各自的用途,有的區域生命週期跟虛擬機器一樣,隨著虛擬機器程式的啟動而存在,伴隨這虛擬機器的程式結束而消亡。而有些區域則依賴使用者執行緒的啟動和結束而建立和銷燬。具體如下圖:

  1. 方法區(Method Area):
    (1)用於儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料;

(2)方法區是各個執行緒共享的記憶體區域,在虛擬機器啟動時建立,因為同一個class類資訊只需要載入一份就夠了;

(3)java虛擬機器規範中把方法區描述為堆記憶體的一個邏輯部分,但它有另外一個別名叫“非堆”,用於與java堆區分開來。在JDK8之前方法區叫做Perm space,在JDK8及以後叫做Metaspace(即後設資料區)。

  1. 堆(Heap):Java堆是被所有執行緒共享,虛擬機器啟動時建立,此記憶體區域唯一的目的就是存放物件例項,在Java虛擬機器規範中的描述是:所有的物件例項以及陣列都要在堆上分配,但是隨著JIT編譯器的發展和逃逸分析技術逐漸成熟,棧上分配,標量替換優化技術將會導致一些微妙的變化發生,所有的物件都分配在堆上也就變得不那麼絕對了。
  1. 虛擬機器棧(Java Virtual Machine Stacks):虛擬機器棧是執行緒私有的或者說是獨有的,隨著執行緒的建立而建立。一個執行緒的執行狀態(正在呼叫哪個方法),就是由這個執行緒對應的虛擬機器棧來儲存的。

每一個被執行緒執行的方法,為虛擬機器棧中的一個棧幀,呼叫一個方法,就會向棧中壓入一個棧幀;一個方法呼叫完成,就會把該棧幀從棧中彈出。如下圖解:

  1. 程式計數器(The Pc Register):我們都知道一個JVM程式中有多個執行緒在執行,而執行緒中的內容是否能夠擁有執行權,是根據CPU排程來的。假如執行緒A正在執行到某個地方,突然失去了CPU的執行權,切換到執行緒B了,然後當執行緒A再獲得CPU執行權的時候,怎麼能繼續執行呢?這就是需要線上程中維護一個變數,記錄執行緒執行到的位置,這就是程式計數器。

  2. 本地方法棧(Native Method Stacks):本地方法棧與虛擬機器棧所發揮的作用非常相似,他們之間的區別不過是虛擬機器棧為虛擬機器執行Java方法(位元組碼)服務,而本地方法棧則為虛擬機器中使用到的native方法服務。即如果當前執行緒執行的方法是Native型別的,這些方法就會在本地方法棧中執行。

總結一下,就JVM的設計規範,從使用用途角度JVM的記憶體大體的分為:執行緒私有記憶體區 和 執行緒共享記憶體區。

執行緒私有記憶體區在類載入器編譯某個class檔案時就確定了執行時需要的“程式計數器”和“虛擬棧幀”等所需的空間,並且會伴隨著當前執行執行緒的產生而產生,執行執行緒的消亡而消亡,因此“執行緒私有記憶體區”並不需要考慮記憶體管理和垃圾回收的問題。

執行緒共享記憶體區在虛擬機器啟動時建立,被所有執行緒共享,是Java虛擬機器所管理記憶體中最應該關注的和最大的一塊。

那麼JVM記憶體模型是如何設計的?JVM又是如何進行記憶體管理(也就是垃圾回收)的?垃圾回收演算法有哪些?目前常用的垃圾回收器又有哪些?我會在下篇文章跟您共同解答這些問題。

作者:宜信技術學院 譚文濤

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69918724/viewspace-2706710/,如需轉載,請註明出處,否則將追究法律責任。

相關文章