JVM2:內存溢出及常用命令列表

前言

本篇博文主要介紹使用程序觸發對應的內存溢出,並附帶上JVM常用的命令,供以後查看使用。

堆溢出

堆主要是用來存儲對象,我們只要不斷的創建對象,並防止虛擬機對對象進行回收則可以觸發堆溢出。

  1. -Xms設置堆最小值、-Xmx設置堆最大值。如果兩者相同,則可以避免堆自動擴展;
  2. -XX:+HeapDumpOnOutOfMemoryError可以讓虛擬機在出現內存溢出異常時Dump出當前的內存堆轉儲快照以便事後進行分析
/**
 * Title:HeapOom 堆溢出
 * Description: VM args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
 *
 * @author lin.xu
 * @date 2017/12/4.
 */
public class HeapOom {

  static class OomObject {

  }

  public static void main(String[] args) {
    List<OomObject> list = new ArrayList<OomObject>();

    while (true) {
      list.add(new OomObject());
    }
  }
}

執行過後,會提示java.lang.OutOfMemoryError: Java heap space

解決方法:

通過內存映像分析工具(如Eclipse Memory Analyzer、JProfiler)對Dump出來的堆轉儲快照進行分析,重點先確定是內存泄漏還是內存溢出。如果是內存泄漏,可進一步通過工具查看泄漏對象到GC Roots的引用鏈,掌握泄漏對象的類型信息及GC Roots引用鏈信息就可以定位泄漏代碼位置;如果不是內存泄漏,則可以檢查-Xms-Xmx參數設置,另外查看下代碼判斷對象生命週期是否過長。

虛擬機棧和本地方法棧溢出

棧內存容量的計算由操作系統爲虛擬機分配的內存,扣除最大堆內存和方法區佔用的最大內存之後所剩餘的,由於程序計數器佔用內存小可以忽略不計。

  1. -Xss設置虛擬機棧大小,-Xoss設置本地方法棧大小。但是對於HotSpot來說,-Xoss是無效的,因爲HotSpot中根本不區分本地方法棧;
/**
 * Title:JavaVmStackSof 棧溢出
 * Description: -Xss180k
 *
 * @author lin.xu
 * @date 2017/12/4.
 */
public class JavaVmStackSof {
  private int stackLength = 1;

  public void stackLeak() {
    stackLength++;
    stackLeak();
  }

  public static void main(String[] args) {
    JavaVmStackSof jvss = new JavaVmStackSof();
    try {
      jvss.stackLeak();
    } catch (Throwable e) {
      System.out.println("Stack size: " + jvss.stackLength);
      throw e;
    }
  }
}

執行過後,會提示java.lang.StackOverflowError

解決方法:

一般可以依據拋出的異常直接定位到代碼的位置。如果是由於過多線程導致的內存溢出,在不能減少線程數的情況下,只能減少最大堆和減少棧容量來換取更多的線程。

方法區和運行時常量池溢出

JDK1.7及之前版本,採用PermSizeMaxPermSize來設置方法區大小,由於JDK1.7開始已經在“去永久代”的實現,在JDK9中如果採用上述兩個配置運行本節的示例則直接提示

Ignoring option PermSize; support was removed in 8.0。
Ignoring option MaxPermSize; support was removed in 8.0

JDK9中需要將PermSize替換爲MetaspaceSizeMaxPermSize替換爲MaxMetaspaceSize

方法區主要用於存放Class相關的信息,如類名、訪問修飾符、常量池、字段描述及方法描述等。測試時主要就是加載大量的類來使方法區溢出。

/**
 * Title:JavaMethodAreaOom
 * Description:vm args: -XX:PermSize=10M -XX:MaxPermSize=10M
 *
 * @author lin.xu
 * @date 2017/12/5.
 */
public class JavaMethodAreaOom {

  public static void main(String[] args) {
    while (true) {
      Enhancer enhancer = new Enhancer();
      enhancer.setSuperclass(OomObject.class);
      enhancer.setUseCache(false);
      enhancer.setCallback(new MethodInterceptor() {
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
            throws Throwable {
          return methodProxy.invokeSuper(o, objects);
        }
      });
      enhancer.create();
    }
  }
}

執行過後,會提示java.lang.OutOfMemoryError: PermGen space

本機直接內存溢出

DirectMemory容量可以通過-XX: MaxDirectMemorySize指定,如果不指定,則默認與Java堆最大值相同。

/**
 * Title:DirectMemoryOom
 * Description:vm args: -Xmx10M/-XX:MaxDirectMemorySize=10M
 *
 * @author lin.xu
 * @date 2017/12/5.
 */
public class DirectMemoryOom {
  private static final int _1MB = 1024 * 1024;

  public static void main(String[] args) throws IllegalAccessException {
    Field field = Unsafe.class.getDeclaredFields()[0];
    field.setAccessible(true);
    Unsafe unsafe = (Unsafe) field.get(null);
    while (true) {
      unsafe.allocateMemory(_1MB);
    }
  }
}

執行過後,會提示java.lang.OutOfMemoryError

由DirectMemory導致的內存溢出,明顯的特徵就是HeapDump文件中不會看見明顯的異常。如果發現OOM之後Dump文件很小,而程序中又間接或直接使用了NIO,則可以考慮檢查下是不是本機直接內存溢出問題。

JVM Args常用命令列表

命令 說明
-Xms20M 設置Java堆最小內存爲20M,必須以M爲單位
-Xmx20M 設置Java堆最大內存爲20M,必須以M爲單位。如果值與-Xms設置的相同,則Java堆不能擴展
-Xss128k 設置JVM棧內存大小爲128K。在JDK9中,最小必須設置爲180k
-Xoss128k 設置本地方法棧大小爲128k。不過在HotSpot虛擬機中,本參數無效。因爲HotSpot中不區分JVM棧和本地方法棧
-XX:PermSize=10M JVM初始分配的永久代容量,必須以M爲單位。JDK8過後已無效。
-XX:MaxPermSize=10M JVM允許分配的永久代最大容量,必須以M爲單位。JDK8過後已無效
-Xnoclassgc 關閉JVM對類的垃圾回收
-XX:+TraceClassLoading 查看類的加載信息
-XX:+TraceClassUnLoading 查看類的卸載信息
-XX:NewRatio=4 年輕代:老年代=1:4
-XX:SurvivorRatio=8 2個Survivor:1個Eden=2:8
-Xmn20M 年輕代大小爲20M
-XX:+HeapDumpOnOutOfMemoryError 程序拋出異常時,存儲堆內存轉儲快照
-XX:+UseG1GC JVM使用G1垃圾收集器
-XX:+PrintGCDetails 在控制檯上打印出GC具體細節
-XX:+PrintGC 在控制檯上打印出GC信息
-XX:PretenureSizeThreshold=3145728 對象大於3145728(3M)時直接進入老年代分配,這裏只能以字節作爲單位
-XX:MaxTenuringThreshold=1 對象年齡大於1,則自動進入老年代
-XX:CompileThreshold=1000 當一個方法被調用1000次之後,被認爲是熱點代碼,觸發即時編譯
-XX:+PrintHeapAtGC 查看每次GC前後堆內存佈局
-XX:+/-UseTLAB 虛擬機是否啓動TLAB
-XX:+PrintTLAB 查看TLAB的使用情況
-XX:+UseSpining 開啓自旋鎖
-XX:+PreBlockSpin 更改自旋鎖的自旋次數,使用這個參數必須先開啓自旋鎖
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章