天天看點

閉關修煉(十五)JVM參數調優Java虛拟機Java記憶體結構堆堆記憶體參數配置棧

文章目錄

  • Java虛拟機
    • 什麼是java虛拟機
  • Java記憶體結構
    • 什麼是java記憶體結構
    • 堆記憶體
  • 堆記憶體參數配置
    • 什麼是虛拟機參數配置?
    • 堆的配置參數
    • 顯示堆參數例子
    • 配置堆參數-初始值和最大值
    • 配置堆參數-設定新生代參數
    • 配置堆參數-設定新生代與老年代比例
    • 堆溢出解決辦法
    • Tomcat記憶體溢出在catalina.sh修改堆記憶體大小
    • 什麼是棧溢出
    • 棧溢出解決方法

Java虛拟機

什麼是java虛拟機

所謂虛拟機,就是一台虛拟的機器,它是一款軟體,用來執行一系列虛拟計算指令,他們完全是對實體計算的仿真,提供了一個可以運作完整作業系統的平台。

java虛拟機,它專門為執行單個計算程式而計算,在java虛拟機中執行的指令我們稱為java位元組碼指令。

在虛拟機上運作的軟體都限制于虛拟機提供的資源中。

Java記憶體結構

什麼是java記憶體結構

JVM虛拟機存儲空間。

java檔案編譯後變為class檔案,classLoader類加載器讀取class檔案,配置設定資源到哪個記憶體空間去。

記憶體空間:

  1. 方法區

    方法區又稱為永久區,static關鍵字修飾的存放在方法區,方法區一般存放常量資訊,當class檔案被加載的時候,方法區就會被初始化,方法區是全局的。

  2. 建立(new)的對象,數組等等,存放在堆記憶體中。堆記憶體所有線程都會共享,

  3. 存放定義基本局部變量,好處是代碼運作完畢,自動釋放記憶體。每個線程私有,互不共享,棧不會産生線程安全問題。類的方法存放在棧中。

  4. 本地方法棧

    主要作用是調用C語言,安卓開發中,底層可能會用C語言,java去使用C語言用到JNI技術,會用到本地方法棧。

PC寄存器:

每個線程都有PC寄存器,每個線程的PC寄存器是私有的。執行指令的指針

執行引擎:

負責執行位元組碼檔案

調優政策主要是堆和垃圾回收機制。

堆記憶體

new出來的對象都會存放在堆記憶體中,堆記憶體中配置設定兩個區,分為新生代和老年代,分代作用是利于垃圾回收機制

新生代,垃圾回收機制不經常回收的區域。

老年代:如果對象被頻繁的使用,将對象放入到老年代中。

新生代裡又分為eden、s0、s1區。其中s0和s1區大小相等,目的是垃圾回收複制算法。其中s0、s1又稱為from/to區

  • eden:剛建立的對象存放在eden區
  • s0、s1:在eden區的對象經常被使用後轉移到s0或者s1區。
  • 老年代:如果s0或者s1區的對象仍在頻繁使用就晉升到老年代區,到了老年代的對象不會回到新生代,是不可逆的轉移過程。

垃圾回收機制主要回收新生代。

調優:盡量減少垃圾回收機制的次數。在web系統中盡量減少常量資訊。盡量減少老年代的回收次數,提高新生代的回收次數。

堆記憶體參數配置

什麼是虛拟機參數配置?

使用給定的參數執行java虛拟機,就可以在系統運作時列印相關日志,用于分析實際問題。

主要圍繞堆、棧、方法區進行配置

堆的配置參數

參數 說明
-XX:+PrintGC 每次觸發GC時列印相關日志
-XX:+UserSerialGC 串行回收
-XX:+PrintGCDetails 更詳細的GC日志
-Xms 堆初始值
-Xmx 堆最大可用值
-Xmn 新生代堆最大可用值
-XX:SurvivorRatio 用來設定新生代中eden空間和trom/to空間的比例

實際工作中,我們一定要把初始的堆大小與最大堆大小相等,這樣的好處是可以減少程式運作時垃圾回收次數,進而提高效率

顯示堆參數例子

import java.text.DecimalFormat;

public class Test1 {
    public static void main(String[] args) throws InterruptedException {
        // 建立byte數組
        byte[] bytes1 = new byte[1024 * 1024];
        System.out.println("配置設定1M記憶體");
        jvmInfo();
        Thread.sleep(5000);
        byte[] bytes2 = new byte[4 * 1024 * 1024];
        System.out.println("配置設定4M記憶體");
        jvmInfo();
    }

    /**
     * 比特轉換為M
     *
     * @param bt 多少比特
     * @return {@link String} 多少M,保留兩位小數
     */
    static public String toM(long bt) {
        float num = (float) bt / (1024 * 1024);
        // 設定小數格式
        DecimalFormat df = new DecimalFormat("0.00");
        return df.format(num);
    }

    /**
     * 顯示jvm資訊
     */
    static public void jvmInfo() {
        // 最大記憶體配置資訊 機關K
        long maxMemory = Runtime.getRuntime().maxMemory();
        System.out.println("最大記憶體:" + toM(maxMemory) + " M");
        // 目前空閑記憶體
        long freeMemory = Runtime.getRuntime().freeMemory();
        System.out.println("目前空閑記憶體:" + toM(freeMemory) + " M");
        // 傳回Java虛拟機中的記憶體總量
        long totalMemory = Runtime.getRuntime().totalMemory();
        System.out.println("記憶體總量:" + toM(totalMemory) + " M");
        // 傳回Java虛拟機可用的處理器數量
        long availableProcessors = Runtime.getRuntime().availableProcessors();
        System.out.println("可用的處理器數量:" + availableProcessors);
    }
}


           

totalMemory()應該是已用記憶體的意思

閉關修煉(十五)JVM參數調優Java虛拟機Java記憶體結構堆堆記憶體參數配置棧

配置堆參數-初始值和最大值

Edit Configurations

閉關修煉(十五)JVM參數調優Java虛拟機Java記憶體結構堆堆記憶體參數配置棧

VM arguments添加參數

-Xms5m -Xmx20m -XX:+PrintGCDetails -XX:+UseSerialGC -XX:+PrintCommandLineFlags

閉關修煉(十五)JVM參數調優Java虛拟機Java記憶體結構堆堆記憶體參數配置棧

運作結果

閉關修煉(十五)JVM參數調優Java虛拟機Java記憶體結構堆堆記憶體參數配置棧

這一條日志表示GC已經回收了

可以看到GC回收了兩次

閉關修煉(十五)JVM參數調優Java虛拟機Java記憶體結構堆堆記憶體參數配置棧

我們把參數 -Xms5m -Xmx20m改成 -Xms20m -Xmx20m,初始值和最大值相等,看看結果,就隻回收了一次

閉關修煉(十五)JVM參數調優Java虛拟機Java記憶體結構堆堆記憶體參數配置棧

為什麼初始值越小,垃圾回收機制次數越多?

初始值越小,當要配置設定較大的記憶體時,空間不夠,配置設定記憶體的時候就越需要進行回收

配置堆參數-設定新生代參數

-Xmn 新生代大小

-XX:SurvivorRatio 設定新生代中eden區與from/to空間的比例

VM arguments添加參數

-Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC

設定新生代大小為2m,eden與from/to比例為2:1

public class Test2 {
    public static void main(String[] args) {
        byte[] bytes = null;
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
            bytes = new byte[1024*1024];
        }
    }
}
           

運作結果,eden與from/to比例為2:1

閉關修煉(十五)JVM參數調優Java虛拟機Java記憶體結構堆堆記憶體參數配置棧

配置堆參數-設定新生代與老年代比例

-XX:NewRation 設定老年代/新生代的大小

基本政策:盡可能将對象留在新生代,減少老年代的GC次數。

VM arguments添加參數

-Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC -XX:NewRatio=2

-XX:NewRatio=2,則Old Generation是 Yong Generation的2倍,即Yong Generation占據記憶體的1/3,垃圾回收機制主要回收新生代,盡量減少垃圾回收機制的次數,一般Yong Generation設成占據記憶體的1/3或者1/4

堆溢出解決辦法

堆溢出問題模拟

VM arguments設定參數

-Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails

-XX:+HeapDumpOnOutOfMemoryError 列印問題溢出問題

建立10M的List

public static void main(String[] args) {
        List<Object> list = new ArrayList<Object>();
        for (int i = 0; i < 10; i++) {
            list.add(new byte[1024 * 1024]);
        }
        System.out.println("建立完畢");
    }
           
閉關修煉(十五)JVM參數調優Java虛拟機Java記憶體結構堆堆記憶體參數配置棧

加堆記憶體大小即可, -Xms30m -Xmx30m

Tomcat記憶體溢出在catalina.sh修改堆記憶體大小

閉關修煉(十五)JVM參數調優Java虛拟機Java記憶體結構堆堆記憶體參數配置棧

JAVA_OPTS:(可選)在執行任何指令時使用的Java運作時選項。JAVA_OPTS中的所有選項應該被Tomcat、停止程序、版本指令等使用。

JAVA_OPTS="-server -Xms800m -Xmx800m -XXNewSize=256M -XX:PermSize=256M -XX:MaxNewSize=512m -XX:MaxPermSize=512m"

什麼是棧溢出

操作變量時無限的遞歸進行調用會造成棧溢出,循環調用方法不會造成棧溢出

棧溢出解決方法

棧溢出問題模拟

public class Test4 {
    static int count = 0;

    public static void getCount() {

        try {
            count++;
            getCount();
        } catch (Throwable e) {
            System.out.println("深度:" + count);
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        getCount();
    }
}

           
閉關修煉(十五)JVM參數調優Java虛拟機Java記憶體結構堆堆記憶體參數配置棧

配置參數隻能增加深度,而不能解決無限遞歸造成的棧溢出問題

-Xss5m 設定最大調用深度

閉關修煉(十五)JVM參數調優Java虛拟機Java記憶體結構堆堆記憶體參數配置棧

從1萬2千多到26萬多

閉關修煉(十五)JVM參數調優Java虛拟機Java記憶體結構堆堆記憶體參數配置棧