目標
為啥面試總愛問jvm的內(nèi)存結(jié)構(gòu)呢?更有甚者,還直接jmm,拽英文???實際工作又很少用到,憑啥總問我會不會呢?不過你問了,這里我就先來答下,哈哈。
本節(jié)主要內(nèi)容,了解jvm內(nèi)存模型,類加載,線程模型
內(nèi)存模型
我喜歡按圖說話:

分析下:
- java文件,通過java源碼編譯器我被翻譯層class形式
- 我變成class形式后,包含三部分
- 結(jié)構(gòu)信息:我的版本、各部分數(shù)量大小
- 元數(shù)據(jù):常量、方法名、類信息等
- 方法信息: 語句、表達式、字節(jié)碼、值棧信息
這時候我就等待java進程工作時來找我了.
可以通過javap -c看下內(nèi)容
public class HelloWorld extends BaseResponseDTO {
private static final int _1M = 1024 * 1024;
private volatile Integer integer=2;
private Boolean t=true;
public static void main(String[] args) throws InterruptedException {
HelloWorld H=new HelloWorld();
H.integer=+1;
H.t=false;
}
}
對應的class文件內(nèi)容如下
public class com.yeepay.power.common.aop.HelloWorld extends com.yeepay.power.common.dto.BaseResponseDTO {
public com.yeepay.power.common.aop.HelloWorld();
Code:
0: aload_0
1: invokespecial #1 // Method com/yeepay/power/common/dto/BaseResponseDTO."<init>":()V
4: aload_0
5: iconst_2
6: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
9: putfield #3 // Field integer:Ljava/lang/Integer;
12: aload_0
13: iconst_1
14: invokestatic #4 // Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
17: putfield #5 // Field t:Ljava/lang/Boolean;
20: return
public static void main(java.lang.String[]) throws java.lang.InterruptedException;
Code:
0: new #6 // class com/yeepay/power/common/aop/HelloWorld
3: dup
4: invokespecial #7 // Method "<init>":()V
7: astore_1
8: aload_1
9: iconst_1
10: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
13: putfield #3 // Field integer:Ljava/lang/Integer;
16: aload_1
17: iconst_0
18: invokestatic #4 // Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
21: putfield #5 // Field t:Ljava/lang/Boolean;
24: return
}
- java進行開始執(zhí)行,首先bootstrap先加載java等核心jar包,然后是ExtensionClassLoder加載java需要的擴展包,最后appClassLoader來加載剛才生成的class文件
這里有個概念叫做雙親委派模型
雙親委派模型工作過程是:如果一個類加載器收到類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器完成。每個類加載器都是如此,只有當父加載器在自己的搜索范圍內(nèi)找不到指定的類時(即ClassNotFoundException),子加載器才會嘗試自己去加載。
加載完所有class,我的服務也起來了,這個時候,有用戶開始訪問我了.這時候又涉及到了jmm模型問題,看我的圖中可以看出,我所創(chuàng)建的類信息、對象數(shù)據(jù)等都存在jvm的主內(nèi)存中(當然jdk8中元數(shù)據(jù)是放到系統(tǒng)內(nèi)存中的),我在處理用戶數(shù)據(jù)時,首先要申請一個線程,讓一個線程來處理用戶工作.
-
我就是那個線程,我的工作生活是這樣子的(可參考文章https://mp.weixin.qq.com/s/dAHaWLiqjkuc8UpoeF6e5A,寫的不錯),我要工作,所以我向jvm申請了1m的工作空間(工作內(nèi)存,可通過-xss配置),看下我的日常工作流:
線程工作流
哈哈,我是有家的孩子,我的壽命很長哦!
-
對于我的工作空間,是這樣子的:
圖片來源網(wǎng)絡
線程的working memory只是cpu的寄存器和高速緩存的抽象描述.cpu在計算的時候,并不總是從內(nèi)存讀取數(shù)據(jù),它的數(shù)據(jù)讀取順序優(yōu)先級 是:寄存器-高速緩存-內(nèi)存。線程耗費的是CPU,線程計算的時候,原始的數(shù)據(jù)來自內(nèi)存,在計算過程中,有些數(shù)據(jù)可能被頻繁讀取,這些數(shù)據(jù)被存儲在寄存器 和高速緩存中,當線程計算完后,這些緩存的數(shù)據(jù)在適當?shù)臅r候應該寫回內(nèi)存。
所以java內(nèi)存模型分為主內(nèi)存,和工作內(nèi)存。主內(nèi)存是所有的線程所共享的,工作內(nèi)存是每個線程自己有一個,不是共享的。
每條線程還有自己的工作內(nèi)存,線程的工作內(nèi)存中保存了被該線程使用到的變量的主內(nèi)存副本拷貝。線程對變量的所有操作(讀取、賦值),都必須在工作內(nèi)存中進行,而不能直接讀寫主內(nèi)存中的變量。不同線程之間也無法直接訪問對方工作內(nèi)存中的變量,線程間變量值的傳遞均需要通過主內(nèi)存來完成,線程、主內(nèi)存、工作內(nèi)存三者之間的交互關(guān)系如下圖:

每個線程通過JLS定義的八個對主存的操作指令:lock,unlock,read,load,use,assign,store,write。這些行為是不可分解的原子操作
參考資料:
https://www.cnblogs.com/wade-luffy/p/6051384.html
https://www.cnblogs.com/wxd0108/p/5479442.html
https://www.cnblogs.com/chihirotan/p/6486436.html

