走進(jìn)JVM
字節(jié)碼
Java 所有的指令有200個(gè)左右,一個(gè)字節(jié)可以存儲(chǔ)256種不同的指令信息,一個(gè)這樣的字節(jié)稱為字節(jié)碼(中間碼)。在代碼的執(zhí)行過程中,JVM將字節(jié)碼解釋執(zhí)行,屏蔽對底層操作系統(tǒng)的依賴,JVM 也可以將字節(jié)碼編譯執(zhí)行,如果是熱點(diǎn)代碼,會(huì)通過JIT 動(dòng)態(tài)地編譯為機(jī)器碼,提高執(zhí)行效率
字節(jié)碼主要指令如下:
加載或存儲(chǔ)指令
在某個(gè)棧幀中,通過指令操作數(shù)據(jù)在虛擬機(jī)棧的局部變量表與操作棧之間來回傳輸,常用指令如下:
將局部變量加載到操作棧中。
從操作棧頂存儲(chǔ)到局部變量表
將常量加載到操作棧頂,這是極為高頻使用的指令
運(yùn)算指令: 對兩個(gè)操作棧幀上的值進(jìn)行運(yùn)算,并把結(jié)果寫入操作棧頂IADD、IMUL
類型轉(zhuǎn)換指令 顯示轉(zhuǎn)換兩種不同的數(shù)值類型
對象創(chuàng)建于訪問指令
創(chuàng)建對象指令 NEW NEWARRAY
訪問屬性指令 GETFIELD、PUTFIELD 等
檢查實(shí)例類型指令
操作棧管理指令
出棧指令
賦值棧頂元素并壓入棧
方法調(diào)用與返回指令
同
詳細(xì)見 P125《碼出高效: JAVA開發(fā)手冊》
Java源文件 ----> 詞法解析-- token流 -> 語法解析 -----> 語義分析 -----> 生成字節(jié)碼 ----> 字節(jié)碼
詳細(xì)見P126
類加載過程
任何程序都需要加載到內(nèi)存中才能與CPU進(jìn)行交流。字節(jié)碼 .class 文件同樣需要家長到內(nèi)存中,才可以實(shí)例化類。ClassLoader 就是提前加載 .class 文件到內(nèi)存中
過程: 加載、鏈接、初始化
加載: 讀取類文件產(chǎn)生的二進(jìn)制流,并轉(zhuǎn)為特定的數(shù)據(jù)結(jié)構(gòu),初步校驗(yàn)cafe babe 魔法值,常量池、文件長度、是否有父類等,然后創(chuàng)建對應(yīng)類的實(shí)例
鏈接包括驗(yàn)證、準(zhǔn)備、解析三個(gè)過程。驗(yàn)證是更詳細(xì)的校驗(yàn),比如final 是否合規(guī),類型是否正確、靜態(tài)變量是否合理等;準(zhǔn)備結(jié)果是為靜態(tài)變量分配內(nèi)存,并設(shè)定默認(rèn)值,解析類和方法確保類與類之間的相互引用正確性,完成內(nèi)存結(jié)構(gòu)布局
初始化結(jié)果,執(zhí)行類構(gòu)造器方法,如果賦值運(yùn)算是通過其他類的靜態(tài)方法來完成的,那么會(huì)馬上解析另一個(gè)類,在虛擬機(jī)棧中執(zhí)行完畢后通過返回值進(jìn)行賦值
內(nèi)存布局
Heap (堆區(qū))
是OOM 故障的主要發(fā)源地,它存儲(chǔ)著幾乎所有的實(shí)例對象,堆由垃圾收集器自動(dòng)回收,堆區(qū)各子線程共享使用。通常它的占用的空間是所有內(nèi)存區(qū)域最大的,但是如果無節(jié)制的創(chuàng)建實(shí)例那么也將會(huì)消耗完內(nèi)存導(dǎo)致OOM??梢栽谶\(yùn)行時(shí)動(dòng)態(tài)的設(shè)置它的大小,-Xms256M - Xmx1024M 表示設(shè)定初始值和最大值。服務(wù)器在運(yùn)行過程中,退空間不斷地?cái)U(kuò)容和回縮,勢必形成不必要的系統(tǒng)壓力,所以在線上生產(chǎn)環(huán)境中,JVM 的Xms和Xmx 設(shè)置為一樣大小,避免在GC 后調(diào)整堆大小時(shí)帶來的額外壓力。
(下圖:這里的放得下指的是當(dāng)創(chuàng)建一個(gè)大對象時(shí)候,內(nèi)存區(qū)域是否能夠容納得下)
Metaspace(元空間)
元空間的前身是Perm區(qū)(被稱為永久代),在JDK7 及之前的版本中才有Perm,現(xiàn)在的版本使用了Metaspace。因?yàn)镻erm 在某些場景下,如果動(dòng)態(tài)加載類過多,容易產(chǎn)生Perm 區(qū)的OOM(為了解決需要設(shè)定參數(shù) -XX:MaxPermSize = 1280m),如果部署到新機(jī)器上,往往會(huì)因?yàn)镴VM 參數(shù)沒有修改導(dǎo)致故障再現(xiàn),不熟悉此應(yīng)用的人很難排查。所以,元空間就誕生了。元空間在本地內(nèi)存中分配。
JVM Stack (虛擬機(jī)棧)
棧的特性是先進(jìn)后出的數(shù)據(jù)結(jié)構(gòu),JVM 是基于棧結(jié)構(gòu)的院系環(huán)境。JVM 中的虛擬機(jī)棧是描述Java方法執(zhí)行的內(nèi)存區(qū)域,它是線程私有的。棧中的元素用于支持虛擬機(jī)進(jìn)行方法調(diào)用,每個(gè)方法從開始調(diào)用到執(zhí)行完成的過程。就是棧幀從入棧到出棧的過程?;顒?dòng)線程中只有位于棧頂?shù)膸攀怯行У?,稱為當(dāng)前棧幀。正在執(zhí)行的方法稱為當(dāng)前方法。
虛擬機(jī)棧通過壓棧和出棧的方式,對每個(gè)方法對應(yīng)的活動(dòng)棧幀進(jìn)行運(yùn)算處理,方法正常執(zhí)行結(jié)束,肯定會(huì)跳轉(zhuǎn)到另一個(gè)棧幀上。在執(zhí)行過程中,如果出現(xiàn)異常,會(huì)進(jìn)行異?;厮?,返回地址通過異常處理表確定。
局部變量表: 存放方法參數(shù)和局部變量的區(qū)域
操作棧 : 一個(gè)初始狀態(tài)為空的桶式結(jié)構(gòu)棧。在方法執(zhí)行過程中,會(huì)有各種指令往棧中寫入和提取信息。
動(dòng)態(tài)連接:每一個(gè)棧幀包含一個(gè)常量池中對當(dāng)前方法的引用,目的是支持方法調(diào)用過程的動(dòng)態(tài)連接
方法返回地址: 方法執(zhí)行有兩種退出情況: 1. 正常退出。2.異常退出。 無論何種退出情況都將返回到方法當(dāng)前被調(diào)用的位置。方法退出的過程相當(dāng)于彈出當(dāng)前棧幀。
本地方法棧
Native Method Stack 在JVM內(nèi)存布局中,也是線程對象私有的。被稱為Native 方法服務(wù),線程開始調(diào)用本地方法時(shí),會(huì)進(jìn)入一個(gè)不再受JVM約束的世界。本地方法可以通過JNI來訪問虛擬機(jī)運(yùn)行時(shí)的數(shù)據(jù)區(qū),甚至可以調(diào)用寄存器,具有和JVM相同的能力和權(quán)限。
程序計(jì)數(shù)寄存器
每一個(gè)線程在創(chuàng)建后,都會(huì)產(chǎn)生自己的程序計(jì)數(shù)器和棧幀,程序計(jì)數(shù)器用來存放執(zhí)行指令的偏移量和行號(hào)指示器,程序的執(zhí)行或者恢復(fù)都要依賴程序計(jì)數(shù)器。程序計(jì)數(shù)器在哥線程之間互不影響,此區(qū)域不會(huì)發(fā)生內(nèi)存溢出異常。
從線程共享的角度來看,堆空間和元空間都所有線程共享的,而虛擬機(jī)棧和本地方法棧,程序計(jì)數(shù)器是線程內(nèi)部私有的。
對象實(shí)例化
實(shí)例化對象過程
確認(rèn)類元信息是否存在。當(dāng)JVM接受到new指令時(shí),首先在metaspace 內(nèi)檢查需要?jiǎng)?chuàng)建的類元信息是否存在。若不存在,那么在雙親委派模式下,使用當(dāng)前類加載器以ClassLoader +包名+類名為key 進(jìn)行查找對應(yīng)的.class 文件,如果沒有找到文件,則拋出ClassNotFoundException 異常,如果找到,則進(jìn)行類加載并生成對應(yīng)的Class 類對象。
分配對象內(nèi)存。首先計(jì)算對象占用空間大小,如果實(shí)例成員變量是引用變量,僅分配引用變量空間即可,即4個(gè)字節(jié)大小,接著在堆中劃分一塊內(nèi)存給新對象。在分配內(nèi)存空間時(shí),需要進(jìn)行同步操作,比如采用CAS失敗重試,區(qū)域加鎖等方式保證分配操作的原子性。
設(shè)定默認(rèn)值。成員變量值都需要設(shè)定默認(rèn)值,即各種不同形式的零值。
設(shè)置對象頭。設(shè)置新對象的哈希碼,GC信息,鎖信息,對象所屬的類元信息等。這個(gè)過程的具體設(shè)置方式取決JVM實(shí)現(xiàn)。
執(zhí)行init 方法。初始化成員變量,執(zhí)行實(shí)例化代碼塊,調(diào)用類的構(gòu)造方法,并把堆內(nèi)對象的首地址賦值給引用變量。
垃圾回收
Java 會(huì)對內(nèi)存進(jìn)行自動(dòng)分配與回收管理,使上層業(yè)務(wù)更加安全,方便地使用內(nèi)存實(shí)現(xiàn)程序邏輯。GC 主要目的是清除不再使用的對象,自動(dòng)釋放內(nèi)存。
標(biāo)記-清除
復(fù)制
標(biāo)記-整理
區(qū)分新老年代(分代收集)
但最重要的是不知道哪些技術(shù)需要重點(diǎn)掌握,學(xué)習(xí)時(shí)頻繁踩坑,最終浪費(fèi)大量時(shí)間,所以有一套實(shí)用的視頻課程用來跟著學(xué)習(xí)是非常有必要的。
為了讓學(xué)習(xí)變得輕松、高效,今天給大家免費(fèi)分享一套阿里架構(gòu)師傳授的一套教學(xué)資源。幫助大家在成為架構(gòu)師的道路上披荊斬棘。
這套視頻課程詳細(xì)講解了(Spring,MyBatis,Netty源碼分析,高并發(fā)、高性能、分布式、微服務(wù)架構(gòu)的原理,JVM性能優(yōu)化、分布式架構(gòu))等這些成為架構(gòu)師必備的內(nèi)容!
而且還把框架需要用到的各種程序進(jìn)行了打包,根據(jù)基礎(chǔ)視頻可以讓你輕松搭建分布式框架環(huán)境,像在企業(yè)生產(chǎn)環(huán)境一樣進(jìn)行學(xué)習(xí)和實(shí)踐。
有需要的可以加群:810589193,點(diǎn)擊鏈接加入群聊【Java架構(gòu)學(xué)習(xí)交流群】:https://jq.qq.com/?_wv=1027&k=5deQUBl