關於JVM的組成與classloader

天启A發表於2024-11-04

JVM有什麼好處:

一次編寫,到處報錯

自動記憶體管理,垃圾回收機制

JVM執行流程:由java原始碼編譯成位元組碼(class)檔案,再由類載入器對class檔案進行裝載,將其加入到執行時資料區;執行時資料區可以呼叫native方法、jit最佳化器、直譯器等

java - v x.class //列印堆疊大小,區域性變數的數量和方法的引數

javap -v x.class //列印類的元空間資訊

JVM組成

1.什麼是程式計數器

每個執行緒都私有一份的,內部儲存位元組碼的行號。用於記錄正在執行的位元組碼指令的地址。

例如A執行緒在執行到第10行時,時間片被B執行緒搶走此時A執行緒會失去執行權,這時程式計數器會記錄下當前行號,當執行權再次被分給A時,A會在上一次執行的基礎上繼續往下執行

2.java中的堆是什麼

堆是執行緒共享的區域,共享物件例項、陣列等,當堆沒有更多的記憶體空間分配給例項,無法再擴充時,就會出現OOM問題

堆內有年輕代、老年代兩部分:年輕代被化為eden、S0、S1;根據JVM策略,在eden中未被垃圾回收的例項會進入S0或S1;在經歷數次回收後仍存在的例項會進入老年代;老年代是儲存一些生命週期長的物件


3.虛擬棧又是什麼

棧的特點是先進後出。線上程執行時所需要的記憶體,即虛擬機器棧。

虛擬機器棧由多個棧幀組成,對應著方法呼叫所佔用的記憶體;棧幀一般包含了引數、區域性變數、返回地址

每個執行緒只能有一個活動棧幀,即正在執行的方法

4.棧記憶體分配是越大越好嗎?

預設幀棧記憶體是1MB,棧幀過大會導致執行緒數變少。

5.方法內的區域性變數是執行緒安全的嗎?

如果是在方法內建立並消費,那麼區域性變數就是安全的。但是如果區域性變數由引用獲得並逃離了方法的作用範圍,那就是不安全的。

6.棧記憶體溢位情況

棧幀過多,棧幀過大(不常見)

7.堆疊的區別是什麼

棧記憶體主要用於儲存區域性變數和方法呼叫;堆記憶體主要用於儲存java物件和陣列。堆會垃圾回收,棧不需要

棧記憶體是執行緒私有的;堆記憶體是執行緒共有的

棧記憶體報錯是stackoverflow;堆記憶體儲存是OOM

8.什麼是方法區method area/元空間metaspace

元空間原屬於堆的永久代,後單獨出來。主要用於儲存類的資訊、執行時常量池。

在java1.7及之前還有永久代,現在被改為了本地記憶體中的metaspace,metaspace主要用於儲存類資訊,靜態變數,常量,編譯後的程式碼。

在動態類載入會越來越多的情況下,永久代會出現記憶體不可控的狀態,一旦記憶體設小極有可能出現OOM問題。因此在java8之後把永久代轉移到了本地記憶體,一定程度上防止了OOM。

但是如果metaspace的記憶體無法滿足分配的請求時,也會報錯oom:Metaspace(預設情況下metaspace的大小是沒有上限的,我們可以透過vm options設定-XX:MaxMetaspaceSize來指定大小)

9.什麼是常量池/執行時常量池

常量池可以看作為一張表,虛擬機器指令根據這張表找到要執行的類名、方法名、引數型別、字面量等

當類被載入時,常量池資訊就會被放入執行時常量池,並把裡面的符號地址轉換為真實地址

10.你聽過直接記憶體嗎

常規IO會在使用者態(java)呼叫核心態,核心套需要呼叫java堆緩衝區,而java堆緩衝區又需要呼叫系統記憶體的緩衝區,由系統記憶體緩衝區來讀取檔案最終返給java

在NIO中,java堆記憶體和系統記憶體是公用的,減少了一層不必要的呼叫,這就是直接記憶體。

直接記憶體不由JVM管理,是由系統記憶體管理的。使用直接記憶體少了一次快取的使用,能夠提高效能。直接記憶體分配讀寫開銷大,且不受JVM記憶體回收管理

類載入器classloader

1.什麼是類載入器,有哪些

JVM只能執行二進位制檔案,類載入器的作用是將位元組碼載入到JVM中,讓java檔案執行起來

一般有四個:

啟動類載入器(bootstrap classloader)載入java_home/jre/lib目錄的庫

擴充套件類載入器(ext classloader)載入java_home/jre/lib/ext目錄的類

應用類載入器(app classloader)載入classpath下的類(一般來說就是我們自己寫的類)

自定義類載入器,tomcat就是基於j2ee實現的

2.什麼是雙親委派模型

載入某一個類,先委託上一級的載入器進行載入,如果上級載入器也有上級,則會繼續向上委託,如果該類委託上級沒有被載入,子載入器嘗試載入該類

使用雙親委派機制,保證類不會重複載入,確保唯一性。同時保證類庫API不會被修改

3.類裝載的執行過程

載入:查詢和匯入class檔案——根據類的二進位制資料流,在metaspace中儲存類的資料結構,在堆中開闢空間儲存其對應的物件

驗證:保證類載入的準確性——對檔案進行檢查,檢視類的檔案格式,語法,位元組碼等相關資訊是否錯誤。同時class檔案會在常量池透過字串記錄自己要使用的方法,檢查他們是否存在

準備:為類變數分配記憶體並設定類變數的初始值——(static變數、static修飾的final的引用型別分配空間在此階段完成,賦值在初始化階段完成;static修飾的final的基本型別與字串常量賦值都在此階段完成)

解析:把類中的符號引用轉換為直接引用

初始化:對類的靜態變數,靜態程式碼塊執行初始化操作——(初始化一個類時,如果父類尚未初始化則優先初始化其父類;同時包含多個靜態方法、變數,則應按照從上到下的順序執行)

使用:JVM開始從入口方法開始執行使用者的程式程式碼

解除安裝:使用者執行完程式後,jvm銷燬建立的class物件

相關文章