這個其實也很正常,其實很多程序員在工作中用到JVM性能調優的情況很少,可以說大部分情況用不到,但用不掉是不是意味著可以不用學習了呢?個人認為還是很有必要學習的,學習JVM其實就像你學習古詩詞一樣,你說學習古詩詞有什么用?至少能提高你的審美以及文化水平吧,那么了解JVM,起碼能讓你更加了解底層代碼的運行原理,提高對技術的認知水平,而且你在簡歷上加上了解JVM性能調優,那么你的簡歷就跟別人區分開來了。
以下是我綜合整理的JVM基礎知識,希望看完能讓你對JVM基礎知識有更深的理解。JVM如何加載class
加載階段(Loading)、
連接階段(Linking)、
初始化階段(Initializing)
加載階段(Loading)
加載階段主要做3件事情:- 通過類全限定名獲取定義此類的二進制字節流;
- 將字節流代表的靜態存儲結構轉換為方法區的運行時數據結構;
- 在內存中生成此類的java.lang.Class對象,作為方法區這個類的各種數據的訪問入口。
連接階段(Linking)
Linking的過程分為三小步:- Verification是校驗裝進來的class文件是不是符合class文件的標準,假如你裝進來的不是這個CAFEBABE,在這個步驟就被拒絕掉了。
- Preparation是把class文件靜態變量賦默認值,不是賦初始值,比如你statici=8,注意這個步驟8并不是把i值賦成8,而是先賦0。
- Resolution是把class文件常量池里面用到的符號引用轉換為直接內存地址,可以直接訪問到的內容。
初始化階段(Initializing)
Initlalizing意思是把靜態變量賦初始值。雙親委派機制
雙親委派是一個孩子向父親方向,然后父親向孩子方向的雙親委派過程。
一個class文件需要被load內存的時候是這樣的任何一個class,假如你定義了ClassLoader,這時候就先嘗試去自定義的ClassLoader里面找,他內部維護著緩存,先問你有沒有幫我把這個class加載進來了?如果加載進來了就不需要加載第二遍了,如果沒有加載進來,那就趕緊把我加載進來。他如果在自定義的ClassLoader緩存中沒有找到的話,他并不是直接加載這個類,他會先去他的父親Application父加載器,說爸爸你有沒有把這個類加載進來了呀?這個時候Application也會去他的緩存里面找有沒有這個類,如果有則返回,如果沒有則委托給他的父親Bootstrap,同理Bootstrap也會去他的緩存中找看有沒有這個類,有則返回,沒有則繼續委托給他的父親Extendsion去加載,這時候Extendsion說我只負責加載擴展jar包里的類,你的類我找不到,麻煩Application去加載,Application又說我只是負責加載classpath路徑下的類,其他的我也找不到,然后再委托給自定義的ClassLoader去加載。整個過程經過了一圈轉了一圈,才真正把這個類加載進來,當我們能夠把這個類加載進來的時候叫做成功,如果加載不進來,拋出異常ClassNotfoundException。這就叫雙親委派。CPU的內存結構以及JavaMemoryModel
CPU計算核心與內存之間有三級緩存,緩存離CPU計算核心越遠容量越大、速度越慢,反之則容量越小、速度越快。CPU計算核心與內存之間存在著三級緩存,那么這個時候就會帶來數據一致性的問題,如果在單線程的環境數據一致性是沒有問題的,但是在多線程環境中就很難保證數據的一致性了。為了解決這個問題,CPU引入了一個數據一致性的協議,這個協議叫MESI。關于MESI協議更詳細的描述,大家可以查看這篇文章【并發編程】MESI--CPU緩存一致性協議Java的內存模型(JavaMemoryModel)其實就是仿照CPU的內存結構來設計和定義的。JMM是一種規范,目的是解決由于多線程通過共享內存進行通信時,存在的本地內存數據不一致、編譯器會對代碼指令重排序、處理器會對代碼亂序執行等帶來的問題。Java內存模型中規定了所有的變量都存儲在主內存中,每個線程有自己的工作內存(類比緩存理解),線程的工作內存中保存了該線程使用到主內存中的變量拷貝,線程對變量的所有操作(讀取、賦值)都必須在工作內存中進行,而不能直接讀寫主內存中的變量。不同線程之間無法直接訪問對方工作內存中的變量,線程間變量值的傳遞(通信)均需要在主內存來完成。Java對象的內存布局
在HotSpot虛擬機中,對象在內存中的存儲布局分為三塊區域:對象頭(Header)實例數據(InstanceData)對齊填充(Padding)一個java對象在內存中占多少個字節呢?- 64位系統(未開啟指針壓縮):MarkWord占用8個字節+ClassPointer占用8個字節=16個字節(16已經是8的整數倍,所以不需要對齊填充)
對象頭的大小:16個字節- 64位系統(開啟指針壓縮):MarkWord占用8個字節+ClassPointer占用4個字節+對齊填充4個字節=16個字節(空對象,所以實例數據大小為0)
對象頭的大小:12個字節以上就是關于JVM的部分基礎知識代碼實驗室的創建已經有一段時間了,建立本號的初衷是為了做技術的分享,通過系統的整理JVM的基礎知識讓大家更加了解Java底層的運行邏輯,要知其然,更要知其所以然。歡迎訂閱我的微信公眾號【Seven的代碼實驗】訂閱更多關于JVM、Spring、以及開源項目的分享,希望能讓你有所收獲。