日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

JVM內存模型深度剖析與優化

作者:bingtanghulu_6 更新時間: 2022-05-11 編程語言

目錄

1. Tomcat類加載器機制

2. jdk體系結構與跨平臺特性

?3. JVM內存結構

3.1 jvm內存指令手冊

?3.2 使用jvisualvm工具查看對象回收

4. jvm常用參數設置

4.1 模擬桟溢出實戰


1. Tomcat類加載器機制

tomcat為什么可以多個war包共存,tomcat每有一個war包都會加載一個war包的類加載器,這個類加載器叫webappclassLoader。每個jsp都會使用自己的類加載器加載,所以不需要編譯就可以生效。

?

?

2. jdk體系結構與跨平臺特性

?jdk體系結構:

jdk:java開發工具包含jre和jvm,比如我們常用的java,javac,javap,jar命令

jre:java運行時環境,包含了我們運行java環境的一些常用核心類庫。

jvm:java虛擬機,java類文件運行都依賴于java虛擬機。

?

?3. JVM內存結構

jvm內存結構大體如下:

1.類加載子系統

2.java運行時環境(*重要)

3.字節碼執行引擎

每個線程獨有的:桟,本地方法桟,程序計數器

? ? ? ? 桟:存放每塊方法獨有的桟幀

? ? ? ? 本地方法桟:調用native方法時需要劃分內存

? ? ? ? 程序計數器:每次做操作,字節碼執行引擎都會對程序計數器進行修改。

共享的:堆,方法區。

????????方法區:靜態變量,常量,類信息

? ? ? ? 堆:年輕代-1/3(eden(8/10),s0(1/10),s1(1/10))+老年代-2/3

STW:stop the world,為了回收垃圾對象停止整個虛擬機,會讓web應用出現卡頓的情況。

Mimor GC:

????????eden->s0:對象創建在eden,eden內存占滿,執行引擎調用GC進程執行mimorGC,清空eden,復制存活對象到S0區,對象分代年齡+1

? ? ? ? eden->s0->s1:eden內存占滿,執行引擎調用GC進程執行mimorGC,清空eden和s0,復制存活對象到S1區,對象分代年齡+1

? ? ? ? eden->s1->s0:eden內存占滿,執行引擎調用GC進程執行mimorGC,清空eden和s1,復制存活對象到S0區,對象分代年齡+1

? ? ? ? 上述步驟執行15次以后會進入老年代,也有可能復制到S0或者S1復制不下直接放入老年代的情況。

Full GC:回收方法區和堆區,如果堆區無法回收對象仍然被GC ROOT引用,這時仍然有對象創建,就會報OOM錯誤。主要對JVM優化就是為了優化這個GC

GC ROOT:程序方法桟中的局部變量,方法區中的常量和靜態變量都適合作為一個GCroot。

桟結構如下:?

每個方法都會在桟中分配一塊內存空間,這個內存空間叫桟幀。桟結構,遵循后進先出結構

桟幀分為:局部變量,操作數棧,動態鏈接,方法出口幾個概念。我們需要使用javap -v class文件 > 1.txt中間中查看。

?

?

/**
 * javap -c Math.class生成文件如下
 * 可以通過閱讀jvm指令手冊查看運行過程
 * Compiled from "Math.java"
 * public class suanshu.Math {
 *   public suanshu.Math();
 *     Code:
 *        0: aload_0
 *        1: invokespecial #1                  // Method java/lang/Object."":()V
 *        4: return
 *
 *   public static void compute();
 *     Code:
 *        0: iconst_1
 *        1: istore_0
 *        2: iconst_2
 *        3: istore_1
 *        4: iload_0
 *        5: iload_1
 *        6: iadd
 *        7: bipush        10
 *        9: imul
 *       10: istore_2
 *       11: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
 *       14: iload_2
 *       15: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
 *       18: return
 *
 *   public static void main(java.lang.String[]);
 *     Code:
 *        0: invokestatic  #4                  // Method compute:()V
 *        3: return
 * }
 */
public class Math {

    public static void compute(){
        int a = 1;
        int b = 2;
        int c = (a+b) * 10;
        System.out.println(c);
    }


    public static void main(String[] args) {
        Math.compute();
    }
}

3.1 jvm內存指令手冊

棧和局部變量操作
將常量壓入棧的指令
aconst_null 將null對象引用壓入棧
iconst_m1 將int類型常量-1壓入棧
iconst_0 將int類型常量0壓入棧
iconst_1 將int類型常量1壓入 操作數棧
iconst_2 將int類型常量2壓入棧
iconst_3 將int類型常量3壓入棧
iconst_4 將int類型常量4壓入棧
iconst_5 將int類型常量5壓入棧
lconst_0 將long類型常量0壓入棧
lconst_1 將long類型常量1壓入棧
fconst_0 將float類型常量0壓入棧
fconst_1 將float類型常量1壓入棧
dconst_0 將double類型常量0壓入棧
dconst_1 將double類型常量1壓入棧
bipush 將一個8位帶符號整數壓入棧
sipush 將16位帶符號整數壓入棧
ldc 把常量池中的項壓入棧
ldc_w 把常量池中的項壓入棧(使用寬索引)
ldc2_w 把常量池中long類型或者double類型的項壓入棧(使用寬索引)
從棧中的局部變量中裝載值的指令
iload 從局部變量中裝載int類型值
lload 從局部變量中裝載long類型值
fload 從局部變量中裝載float類型值
dload 從局部變量中裝載double類型值
aload 從局部變量中裝載引用類型值(
refernce)
iload_0 從局部變量0中裝載int類型值
iload_1 從局部變量1中裝載int類型值
iload_2 從局部變量2中裝載int類型值
iload_3 從局部變量3中裝載int類型值
lload_0 從局部變量0中裝載long類型值
lload_1 從局部變量1中裝載long類型值
lload_2 從局部變量2中裝載long類型值
lload_3 從局部變量3中裝載long類型值
fload_0 從局部變量0中裝載float類型值
fload_1 從局部變量1中裝載float類型值 fload_2 從局部變量2中裝載float類型值
fload_3 從局部變量3中裝載float類型值
dload_0 從局部變量0中裝載double類型值
dload_1 從局部變量1中裝載double類型值
dload_2 從局部變量2中裝載double類型值
dload_3 從局部變量3中裝載double類型值
aload_0 從局部變量0中裝載引用類型值
aload_1 從局部變量1中裝載引用類型值
aload_2 從局部變量2中裝載引用類型值
aload_3 從局部變量3中裝載引用類型值
iaload 從數組中裝載int類型值
laload 從數組中裝載long類型值
faload 從數組中裝載float類型值
daload 從數組中裝載double類型值
aaload 從數組中裝載引用類型值
baload 從數組中裝載byte類型或boolean類型值
caload 從數組中裝載char類型值
saload 從數組中裝載short類型值
將棧中的值存入局部變量的指令
istore 將int類型值存入局部變量
lstore 將long類型值存入局部變量
fstore 將float類型值存入局部變量
dstore 將double類型值存入局部變量
astore 將將引用類型或returnAddress類型值存入局部變量
istore_0 將int類型值存入局部變量0
istore_1 將int類型值存入局部變量1
istore_2 將int類型值存入局部變量2
istore_3 將int類型值存入局部變量3
lstore_0 將long類型值存入局部變量0
lstore_1 將long類型值存入局部變量1
lstore_2 將long類型值存入局部變量2
lstore_3 將long類型值存入局部變量3
fstore_0 將float類型值存入局部變量0
fstore_1 將float類型值存入局部變量1
fstore_2 將float類型值存入局部變量2
fstore_3 將float類型值存入局部變量3
dstore_0 將double類型值存入局部變量0
dstore_1 將double類型值存入局部變量1 dstore_2 將double類型值存入局部變量2
dstore_3 將double類型值存入局部變量3
astore_0 將引用類型或returnAddress類型值存入局部變量0
astore_1 將引用類型或returnAddress類型值存入局部變量1
astore_2 將引用類型或returnAddress類型值存入局部變量2
astore_3 將引用類型或returnAddress類型值存入局部變量3
iastore 將int類型值存入數組中
lastore 將long類型值存入數組中
fastore 將float類型值存入數組中
dastore 將double類型值存入數組中
aastore 將引用類型值存入數組中
bastore 將byte類型或者boolean類型值存入數組中
castore 將char類型值存入數組中
sastore 將short類型值存入數組中
wide指令
wide 使用附加字節擴展局部變量索引
通用(無類型)棧操作
nop 不做任何操作
pop 彈出棧頂端一個字長的內容
pop2 彈出棧頂端兩個字長的內容
dup 復制棧頂部一個字長內容
dup_x1 復制棧頂部一個字長的內容,然后將復制內容及原來彈出的兩個字長的內容壓入
dup_x2 復制棧頂部一個字長的內容,然后將復制內容及原來彈出的三個字長的內容壓入
dup2 復制棧頂部兩個字長內容
dup2_x1 復制棧頂部兩個字長的內容,然后將復制內容及原來彈出的三個字長的內容壓入
dup2_x2 復制棧頂部兩個字長的內容,然后將復制內容及原來彈出的四個字長的內容壓入
swap 交換棧頂部兩個字長內容
類型轉換
i2l 把int類型的數據轉化為long類型
i2f 把int類型的數據轉化為float類型
i2d 把int類型的數據轉化為double類型
l2i 把long類型的數據轉化為int類型
l2f 把long類型的數據轉化為float類型
l2d 把long類型的數據轉化為double類型 f2i 把float類型的數據轉化為int類型
f2l 把float類型的數據轉化為long類型
f2d 把float類型的數據轉化為double類型
d2i 把double類型的數據轉化為int類型
d2l 把double類型的數據轉化為long類型
d2f 把double類型的數據轉化為float類型
i2b 把int類型的數據轉化為byte類型
i2c 把int類型的數據轉化為char類型
i2s 把int類型的數據轉化為short類型
整數運算
iadd 執行int類型的加法
ladd 執行long類型的加法
isub 執行int類型的減法
lsub 執行long類型的減法
imul 執行int類型的乘法
lmul 執行long類型的乘法
idiv 執行int類型的除法
ldiv 執行long類型的除法
irem 計算int類型除法的余數
lrem 計算long類型除法的余數
ineg 對一個int類型值進行取反操作
lneg 對一個long類型值進行取反操作
iinc 把一個常量值加到一個int類型的局部變量上
邏輯運算
移位操作
ishl 執行int類型的向左移位操作
lshl 執行long類型的向左移位操作
ishr 執行int類型的向右移位操作
lshr 執行long類型的向右移位操作
iushr 執行int類型的向右邏輯移位操作
lushr 執行long類型的向右邏輯移位操作
按位布爾運算
iand 對int類型值進行“邏輯與”操作
land 對long類型值進行“邏輯與”操作
ior 對int類型值進行“邏輯或”操作
lor 對long類型值進行“邏輯或”操作
ixor 對int類型值進行“邏輯異或”操作
lxor 對long類型值進行“邏輯異或”操作 浮點運算
fadd 執行float類型的加法
dadd 執行double類型的加法
fsub 執行float類型的減法
dsub 執行double類型的減法
fmul 執行float類型的乘法
dmul 執行double類型的乘法
fdiv 執行float類型的除法
ddiv 執行double類型的除法
frem 計算float類型除法的余數
drem 計算double類型除法的余數
fneg 將一個float類型的數值取反
dneg 將一個double類型的數值取反
對象和數組
對象操作指令
new 創建一個新對象
checkcast 確定對象為所給定的類型
getfield 從對象中獲取字段
putfield 設置對象中字段的值
getstatic 從類中獲取靜態字段
putstatic 設置類中靜態字段的值
instanceof 判斷對象是否為給定的類型
數組操作指令
newarray 分配數據成員類型為基本上數據類型的新數組
anewarray 分配數據成員類型為引用類型的新數組
arraylength 獲取數組長度
multianewarray 分配新的多維數組
控制流
條件分支指令
ifeq 如果等于0,則跳轉
ifne 如果不等于0,則跳轉
iflt 如果小于0,則跳轉
ifge 如果大于等于0,則跳轉
ifgt 如果大于0,則跳轉
ifle 如果小于等于0,則跳轉
if_icmpcq 如果兩個int值相等,則跳轉
if_icmpne 如果兩個int類型值不相等,則跳轉
if_icmplt 如果一個int類型值小于另外一個int類型值,則跳轉 if_icmpge 如果一個int類型值大于或者等于另外一個int類型值,則跳轉
if_icmpgt 如果一個int類型值大于另外一個int類型值,則跳轉
if_icmple 如果一個int類型值小于或者等于另外一個int類型值,則跳轉
ifnull 如果等于null,則跳轉
ifnonnull 如果不等于null,則跳轉
if_acmpeq 如果兩個對象引用相等,則跳轉
if_acmpnc 如果兩個對象引用不相等,則跳轉
比較指令
lcmp 比較long類型值
fcmpl 比較float類型值(當遇到NaN時,返回-1)
fcmpg 比較float類型值(當遇到NaN時,返回1)
dcmpl 比較double類型值(當遇到NaN時,返回-1)
dcmpg 比較double類型值(當遇到NaN時,返回1)
無條件轉移指令
goto 無條件跳轉
goto_w 無條件跳轉(寬索引)
表跳轉指令
tableswitch 通過索引訪問跳轉表,并跳轉
lookupswitch 通過鍵值匹配訪問跳轉表,并執行跳轉操作
異常
athrow 拋出異常或錯誤
finally子句
jsr 跳轉到子例程
jsr_w 跳轉到子例程(寬索引)
rct 從子例程返回
方法調用與返回
方法調用指令
invokcvirtual 運行時按照對象的類來調用實例方法
invokespecial 根據編譯時類型來調用實例方法
invokestatic 調用類(靜態)方法
invokcinterface 調用接口方法
方法返回指令
ireturn 從方法中返回int類型的數據
lreturn 從方法中返回long類型的數據
freturn 從方法中返回float類型的數據
dreturn 從方法中返回double類型的數據
areturn 從方法中返回引用類型的數據
return 從方法中返回,返回值為void 線程同步
montiorenter 進入并獲取對象監視器
monitorexit 釋放并退出對象監視器
JVM指令助記符
變量到操作數棧:iload,iload_,lload,lload_,fload,fload_,dload,dload_,aload,aload_
操作數棧到變量:
istore,istore_,lstore,lstore_,fstore,fstore_,dstore,dstor_,astore,astore_
常數到操作數棧:
bipush,sipush,ldc,ldc_w,ldc2_w,aconst_null,iconst_ml,iconst_,lconst_,fconst_,dconst_
加:iadd,ladd,fadd,dadd
減:isub,lsub,fsub,dsub
乘:imul,lmul,fmul,dmul
除:idiv,ldiv,fdiv,ddiv
余數:irem,lrem,frem,drem
取負:ineg,lneg,fneg,dneg
移位:ishl,lshr,iushr,lshl,lshr,lushr
按位或:ior,lor
按位與:iand,land
按位異或:ixor,lxor
類型轉換:i2l,i2f,i2d,l2f,l2d,f2d(放寬數值轉換)
i2b,i2c,i2s,l2i,f2i,f2l,d2i,d2l,d2f(縮窄數值轉換)
創建類實便:new
創建新數組:newarray,anewarray,multianwarray
訪問類的域和類實例域:getfield,putfield,getstatic,putstatic
把數據裝載到操作數棧:baload,caload,saload,iaload,laload,faload,daload,aaload
從操作數棧存存儲到數組:
bastore,castore,sastore,iastore,lastore,fastore,dastore,aastore
獲取數組長度:arraylength
檢相類實例或數組屬性:instanceof,checkcast
操作數棧管理:pop,pop2,dup,dup2,dup_xl,dup2_xl,dup_x2,dup2_x2,swap
有條件轉移:ifeq,iflt,ifle,ifne,ifgt,ifge,ifnull,ifnonnull,if_icmpeq,if_icmpene,
if_icmplt,if_icmpgt,if_icmple,if_icmpge,if_acmpeq,if_acmpne,lcmp,fcmpl
fcmpg,dcmpl,dcmpg
復合條件轉移:tableswitch,lookupswitch
無條件轉移:goto,goto_w,jsr,jsr_w,ret
調度對象的實便方法:invokevirtual
調用由接口實現的方法:invokeinterface
調用需要特殊處理的實例方法:invokespecial 調用命名類中的靜態方法:invokestatic
方法返回:ireturn,lreturn,freturn,dreturn,areturn,return
異常:athrow
finally關鍵字的實現使用:
jsr,jsr_w,ret

?3.2 使用jvisualvm工具查看對象回收

在cmd窗口中輸入jvisualvm,寫一個死循環代碼查看內存回收情況。

?

import java.util.ArrayList;

public class Math {


    public static void main(String[] args) throws InterruptedException {
        ArrayList mathArrayList = new ArrayList();
        while(true){
            mathArrayList.add(new Math());
            Thread.sleep(10);
        }
    }
}

4. jvm常用參數設置

常用參數設置:java -Xms2048m -xmx2048m -xmn1024m -xss512k -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -jar jar包

方法區的元空間設置:默認21M,如果達到21M以后會進行Full GC,回收堆和方法區,如果回收后的內存房間很小,下次的方法區內存會變小,如果內存無法回收會進行擴容,這個機制叫動態擴容機制。所以一般我們都設置一下默認大小和最大大小。防止元空間把內存條撐爆。

-Xss:桟中線程內存大小,因為元空間和桟都是用的直接內存,如果值設置的越大,有可能線程使用的內存越大,內存總有用完的一天會導致無法開啟更多線程,默認1M,如果設置越小,反而可以開啟更多線程。

?

4.1 模擬桟溢出實戰

?寫一個遞歸調用,讓桟中不停往下壓方法創建內存,把桟撐爆。

/**
 * 默認1M
 */
public class StackOverFlowTest {

    static int count = 0;

    public static void compute(){
        count ++;
        compute();
    }

    public static void main(String[] args) {
        try{
            compute();
        }catch (Throwable e){
            e.printStackTrace();
            System.out.println("調用次數:"+count);
        }
    }
}

?

原文鏈接:https://blog.csdn.net/qq_21575929/article/details/124157258

欄目分類
最近更新