java 內存模型

為什麼現在開發中強調JVM

1、當前的互聯網開發環境有直接的關係:已經不再單獨的面對傳統的一台主機運行一些程序,而後在進行簡單的維護,現在講究的是:高並發、分佈式、高可用,對於程序的調優裏面就需要去考慮JVM參數設計、JUC的使用。

  【面試必問內容】Java架構師(基礎能力):框架設計+通訊+多線程(JUC)+JVM+數據結構+良好的結構設計(需要大量的代碼基礎)

2、需要清楚內存模型、虛擬機分類、運行模式

3、不適當的JVM運行狀態,有可能會浪費你的電腦性能、良好的JVM調優,可以增加你電腦處理的負載

4、GC處理流程以及常見的算法(JDK1.8、JDK1.9-JDK1.11)

Java程序執行流程

雙親加載機制:系統類由系統類加載負責,而自定義類由其他的類加載器負責。

Java運行時數據區(內存問題)

1、方法區:最重要的內存區域,多線程內存共享,保存了類的信息(名稱、成員、接口、父類),反射機制是重要組成部分,動態進行類操作的實現。

2、堆內存(Heap):保存對象的真實信息,該內存牽扯到釋放問題(GC);

3、棧內存(Stack):線程的私有空間,在每一次進行方法調用的時候都會存在有棧幀,採用先進後出的設計原則;

  3.1、本地變量表:局部參數或形參,允許保存有32位的插槽(Solt),如果超過了32位的長度,需要開闢兩個連續性的插槽(long、double)–volatile關鍵字問題

  3.2、操作數棧:執行所有的方法計算操作

  3.3、常量池引用:String類、Integer類實例

  3.4、返回地址:方法執行完畢後的恢復執行點

4、程序計數器:執行指令的一個順序編碼,該區域的所佔比率幾乎可以忽略;

5、本地方法棧:與棧內存功能類似,區別在於是為本地方法服務的

JVM分類

  Java是直接通過指針進行的程序訪問,所以它沒有採用句柄的形式操作,這樣使得程序的性能更高。

  傳統意義上來講,JVM一共分為三種(虛擬機是一個公共標準)

    【SUN】從JDK1.2開始使用了HotSpot虛擬機標準(2006年開源,利用C++實現、一些JNI部分使用的是系統提供C程序實現的、JIT即時編譯器);

    【BEA】使用了JRockit虛擬機標準,例如WebLogic;

    【IBM】開發了JVM』s(J9)虛擬機;

  Oracle後來通過收購得到了:SUN與BEA,那麼Oracle有了兩個虛擬機標準(不可能浪費兩個研發團隊去干相同的事情);

HotSpot

java version "13.0.1" 2019-10-15
Java(TM) SE Runtime Environment (build 13.0.1+9)
Java HotSpot(TM) 64-Bit Server VM (build 13.0.1+9, mixed mode, sharing)

虛擬機提供三種處理模式:

  1、混合模式:java -version

  2、【禁用JIT】純解釋模式java -Xint -version

  3、純編譯模式java -Xcomp -version

運行模式:

  1、【client】客戶端運行啟動速度,但中間程序的執行慢,佔用內存小

  2、【server】服務器端運行啟動速度,佔用內存多,執行效率高

  3、修改路徑:

控制台:cd /Library/Java/JavaVirtualMachines/jdk-13.0.1.jdk/Contents/Home/lib

Java內存模型(取消伸縮區)(重要)

1、合理的內存模型可以使GC的性能更加強大,不必太大的浪費服務器的性能,從而減少阻塞所帶來的程序的性能影響。

  例:你現在收拾屋子,基本上會有兩類收拾方法:

    方式一:簡單的進行物品的碼放以及打掃衛生,時間短;

    方式二:房屋裝修與改造,時間長。

2、Java中數據保存的內存位置:堆內存(調優、原理);

  最需要強調的就是JDK1.8之後所帶來的內存結構改變以及GC的策略提升;

1.8以後

JVM1.8以前

  當內存不足的時候就需要進行伸縮區的控制,當內存充足的時候就需要考慮將伸縮區所佔用的內存釋放掉(收起),一定會造成額外計算性能的影響,導致程序的整體性能下降。既然已經確定了當前可能是高並發的訪問,並且我的程序獨立的在一台服務器上,那麼這台服務器的資源就應該全部給我去使用。

    public static void main(String[] args) {
        System.out.println("MAX_MEMORY;"+byteToM(Runtime.getRuntime().maxMemory()));
        System.out.println("TOTAL_MEMORY;"+byteToM(Runtime.getRuntime().totalMemory()));
    }
    public static double round(double num,int scale) {
        return Math.round(Math.pow(10, scale)*num)/Math.pow(10, scale);
    }
    public static double byteToM(long num) {
        return round(num/1024/1024, 2);
    }

——初始打印結果
MAX_MEMORY;4096.0
TOTAL_MEMORY;258.0
我現在的電腦內存為16G,所以會發現默認的內容:
MaxMemory:整體電腦內存的1/4
ToTalMemory:整體電腦的1/64

伸縮區的空間:MaxMemory – TotalMemory = 空間好大

程序執行的時候設置有響應的執行參數(-Xmx10G -Xms10G)(取消伸縮區):
-Xmx:分配的最大初始化內存
-Xms:最大的分配內存

java -Xmx大小單位 -Xms大小單位 類文件
——取消伸縮區打印結果
MAX_MEMORY;10240.0
TOTAL_MEMORY;10240.0

GC處理流程(重要)

 1、對象的實例化需要依據關鍵字new完成,所有新對象都會在伊甸園區開闢,若果伊甸園區內存不足會發生MinorGC。

  Member mem=new Member();很小,直接保存在伊甸園;

2、伊甸園區不是無限大的,所有肯定有些對象執行了N次MinorGC後還會存在,那麼這些對象將進入到存活區(存活區有兩個,一個負責保存存活對象,一個負責晉陞,永遠都有一個是空的內存);

3、如果經歷過若干次的MinorGC回收處理之後發現空間依然不夠使用的,那麼則進行老年代的GC回收,執行了一個MajorGC(Full GC,性能很差),如果可以回收空間,則繼續進行MinorGC;

4、如果MajorGC失敗,則繼續內存已經佔用完滿,則拋出OOM異常。

5、如果新創建的對象的空間佔用過大將直接保存到老年代中。

        String str="hello";
        for (int x=0;x<Integer.MAX_VALUE;x++){
            str+=str.intern();
        }

添加一些參數:-XX:+PrintGCDetails

  JDK 1.8的時候默認會根據系統的不同而選擇不同個GC回收策略

  JDK 1.9~默認的採用GC操作就是G1

內存回收算法

1、年輕代回收算法

  1.1、「複製」清理算法,將保留的對象複製到存活區之中,存活區的內容會保存到老年代之中;

  1.2、伊甸園區總是會有大量的新對象產生,所以HotSpot虛擬機使用了BTP(單一CPU的時代所有對象依次保存)、TLAB(拆分不同的塊,依據CPU的核心個數拆分)技術形式進行處理

2、老年代回收算法

  2.1、「標記-清除」算法:先進行對象的第一次標記,在這段時間之內會暫停程序的執行(如果標記的時間長或者對象的內容過多),這個暫停的時間就會長

    2.1.1、就會產生串行標記、並行標記使用問題

  2.2、「標記-壓縮」算法:將零散的內存空間進行整理重新集合再分配

G1算法

1、支持大內存(4-64G)、支持多CPU,減少停頓時間,可以保證並髮狀態下的程序的執行。

使用方法

JDK1.8:-XX:+UseG1GC
JDK 11之後默認就是G1回收器,對於其他的回收算法實際上就可以忽略掉了

Tomcat調優

使用內存的調優

路徑:/tomcat/bin

編輯:catalina.sh

追加配置參數JAVA_OPTS=”-Xms4096m -Xmx4096m -Xss1024k -XX:+UseG1GC”

Tags: