1. 程式人生 > >JVM記憶體分配與回收學習(2)

JVM記憶體分配與回收學習(2)

1、垃圾收集器什麼時候開始回收?

(1)新生代有一個Eden區和兩個survivor區(From survivor 和To Survivor),每次使用Eden和其中一個Survivor(From Survivor),建立物件時,首先會將物件放入Eden區,如果放不下就會引發一次發生在新生代的minor GC(清理Eden和From Survivor空間),將存活的物件複製到 To survivor空間中(如果Eden區和From Survivor區存活的物件大於To survivor區的大小,怎麼辦?答:直接放入老年代區),然後清空Eden和 From survivor區的記憶體。

(2)大物件以及長期存活的物件直接進入老年區。(大物件怎麼判斷?建立時,Eden區放不下的物件,都是大物件

(3)當每次執行minor GC的時候應該對要晉升到老年代的物件進行分析,如果這些馬上要到老年區的物件的大小超過了老年代的剩餘大小,那麼執行一次Full GC以儘可能地獲得老年代的空間。

2、對什麼東西:從GC Roots搜尋不到,而且經過一次標記清理之後仍沒有復活的物件。

3、做了什麼: 

新生代:採用複製演算法來回收新生代,新生代中Eden區和From Survivior區兩部分的記憶體不是按照1:1來劃分,而是按照一定的比例,預設是8:1。 為什麼新生代採用複製演算法?新生代中絕大部分物件符合‘朝生夕死’的特點,存活率低,適合用複製演算法。在存活率高的情況下,來回複製,效率比較低。老年代物件的存活率比較高,不能用複製演算法。

老年代:用“標記-整理”演算法。老年代中的物件經過多次GC,存活率高,不適合用複製演算法。

永久代:存放Java中的類和載入類的類載入器本身。

4、程式碼驗證

注1:以下程式碼中的VM引數

-Xmx20m -Xms20m  -XX:+HeapDumpOnOutOfMemoryError

-XX:HeapDumpPath=d:\dump -XX:+PrintGCDetails -Xmn10M

注2:GC日誌說明

GC後方括號內的資料代表 “GC前該記憶體區域已使用的容量->GC後該記憶體區域已使用的容量(該記憶體區域總容量)”

在方括號之外代表的是“GC前Java堆已使用容量->GC後Java堆已使用容量(Java堆總容量)”。

(1)、情況1:大物件直接放入老年代。

          首先我們驗證大物件臨界的狀態。我們沒有配置新生代中Eden與Survivor的比例,那麼會取預設值8:1,即Eden區為8M,兩個survivor區域為1M。設定a物件大小為8*1000*1024,如下。

package com.xtli.jvm;
public class TestOutOfMemory {
    
     public static void main(String[] args) {
          int v = 0;
          while(v<1) {
              byte[] a = new byte[8*1000*1024];
              //System.out.println(v++);
              v++;
          }
     }
}

程式碼執行後,如下所示

[GC[DefNew: 876K->430K(9216K), 0.0018120 secs] 876K->430K(19456K), 0.0018792 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
 def new generation   total 9216K, used 8622K [0x33400000, 0x33e00000, 0x33e00000)
  eden space 8192K, 100% used [0x33400000, 0x33c00000, 0x33c00000)
  from space 1024K,  42% used [0x33d00000, 0x33d6bac8, 0x33e00000)
  to   space 1024K,   0% used [0x33c00000, 0x33c00000, 0x33d00000)
 tenured generation   total 10240K, used 0K [0x33e00000, 0x34800000, 0x34800000)
   the space 10240K,   0% used [0x33e00000, 0x33e00000, 0x33e00200, 0x34800000)
 compacting perm gen  total 12288K, used 182K [0x34800000, 0x35400000, 0x38800000)
   the space 12288K,   1% used [0x34800000, 0x3482dbf8, 0x3482dc00, 0x35400000)
    ro space 10240K,  44% used [0x38800000, 0x38c7c1d8, 0x38c7c200, 0x39200000)
    rw space 12288K,  52% used [0x39200000, 0x398440c0, 0x39844200, 0x39e00000)

在建立物件a之前出發一次GC,GC後Eden區足夠,將物件a放在了Eden區,此時Eden space used 將近100%,如果a物件設定為9*1024*1024大小時,屬於大物件,將會直接放在老年代區域。如下所示

package com.xtli.jvm;
public class TestOutOfMemory {
     
     public static void main(String[] args) {
          int v = 0;
          while(v<1) {
              byte[] a = new byte[9*1024*1024];
              System.out.println(v++);
          }
     }
}

程式碼執行後,如下所示

0
Heap
 def new generation   total 9216K, used 1040K [0x33400000, 0x33e00000, 0x33e00000)
  eden space 8192K,  12% used [0x33400000, 0x33504160, 0x33c00000)
  from space 1024K,   0% used [0x33c00000, 0x33c00000, 0x33d00000)
  to   space 1024K,   0% used [0x33d00000, 0x33d00000, 0x33e00000)
 tenured generation   total 10240K, used 9216K [0x33e00000, 0x34800000, 0x34800000)
   the space 10240K,  90% used [0x33e00000, 0x34700010, 0x34700200, 0x34800000)
 compacting perm gen  total 12288K, used 183K [0x34800000, 0x35400000, 0x38800000)
   the space 12288K,   1% used [0x34800000, 0x3482dc60, 0x3482de00, 0x35400000)
    ro space 10240K,  44% used [0x38800000, 0x38c7c1d8, 0x38c7c200, 0x39200000)
    rw space 12288K,  52% used [0x39200000, 0x398440c0, 0x39844200, 0x39e00000)

注意“ tenured generation   total 10240K, used 9216K [0x33e00000, 0x34800000, 0x34800000)
   the space 10240K,  90% used [0x33e00000, 0x34700010, 0x34700200, 0x34800000)”說明物件直接放入了老年代。

(2)、情況2:Eden區足夠,將物件直接放入了Eden區

package com.xtli.jvm;
public class TestOutOfMemory {
     
     public static void main(String[] args) {
          int v = 0;
          while(v<1) {
              byte[] a = new byte[1*1024*1024];
              System.out.println(v++);
          }
     }
}

程式碼執行後,如下所示,物件直接放入了新生代的Eden區(Eden區還有其他物件,所以所用記憶體比1024*1024大)

0
Heap
 def new generation   total 9216K, used 2064K [0x33400000, 0x33e00000, 0x33e00000)
  eden space 8192K,  25% used [0x33400000, 0x33604170, 0x33c00000)
  from space 1024K,   0% used [0x33c00000, 0x33c00000, 0x33d00000)
  to   space 1024K,   0% used [0x33d00000, 0x33d00000, 0x33e00000)
 tenured generation   total 10240K, used 0K [0x33e00000, 0x34800000, 0x34800000)
   the space 10240K,   0% used [0x33e00000, 0x33e00000, 0x33e00200, 0x34800000)
 compacting perm gen  total 12288K, used 183K [0x34800000, 0x35400000, 0x38800000)
   the space 12288K,   1% used [0x34800000, 0x3482dc60, 0x3482de00, 0x35400000)
    ro space 10240K,  44% used [0x38800000, 0x38c7c1d8, 0x38c7c200, 0x39200000)
    rw space 12288K,  52% used [0x39200000, 0x398440c0, 0x39844200, 0x39e00000)

(3)情況3:物件在Eden放不下時,發生GC。

        此次迴圈產生8個1024*1024的物件,如下

package com.xtli.jvm;
public class TestOutOfMemory {
     
     public static void main(String[] args) {
          int v = 0;
          while(v<8) {
              byte[] a = new byte[1*1024*1024];
              System.out.println(v++);
          }
     }
}

 結果如下

0
1
2
3
4
5
6
[GC[DefNew: 8044K->431K(9216K), 0.0015837 secs] 8044K->431K(19456K), 0.0016484 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
7
Heap
 def new generation   total 9216K, used 1709K [0x33400000, 0x33e00000, 0x33e00000)
  eden space 8192K,  15% used [0x33400000, 0x3353f960, 0x33c00000)
  from space 1024K,  42% used [0x33d00000, 0x33d6bd20, 0x33e00000)
  to   space 1024K,   0% used [0x33c00000, 0x33c00000, 0x33d00000)
 tenured generation   total 10240K, used 0K [0x33e00000, 0x34800000, 0x34800000)
   the space 10240K,   0% used [0x33e00000, 0x33e00000, 0x33e00200, 0x34800000)
 compacting perm gen  total 12288K, used 183K [0x34800000, 0x35400000, 0x38800000)
   the space 12288K,   1% used [0x34800000, 0x3482dc60, 0x3482de00, 0x35400000)
    ro space 10240K,  44% used [0x38800000, 0x38c7c1d8, 0x38c7c200, 0x39200000)
    rw space 12288K,  52% used [0x39200000, 0x398440c0, 0x39844200, 0x39e00000)

       前7次物件能放入Eden區,未發生GC。但當第8次準備放入Eden區時,發現Eden區記憶體不夠,JVM觸發一次GC操作。GC後,物件可以放入Eden區。將存活的物件(431k大小 = 1024k*42%)放入survivor區域。

(4)情況4。物件在Eden區放不下,不會將物件放入了survivor區。而是發生GC。對比情況3.

package com.xtli.jvm;
public class TestOutOfMemory {
     
     public static void main(String[] args) {
          int v = 0;
          while(v<8) {
              if(v==7) {
                   byte[] a = new byte[200*1024];
              } else {
                   byte[] a = new byte[1*1024*1024];
              }
              System.out.println(v++);
          }
     }
}

結果如下

0
1
2
3
4
5
6
[GC[DefNew: 8044K->431K(9216K), 0.0010873 secs] 8044K->431K(19456K), 0.0011343 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
7
Heap
 def new generation   total 9216K, used 885K [0x33400000, 0x33e00000, 0x33e00000)
  eden space 8192K,   5% used [0x33400000, 0x33471960, 0x33c00000)
  from space 1024K,  42% used [0x33d00000, 0x33d6bd20, 0x33e00000)
  to   space 1024K,   0% used [0x33c00000, 0x33c00000, 0x33d00000)
 tenured generation   total 10240K, used 0K [0x33e00000, 0x34800000, 0x34800000)
   the space 10240K,   0% used [0x33e00000, 0x33e00000, 0x33e00200, 0x34800000)
 compacting perm gen  total 12288K, used 183K [0x34800000, 0x35400000, 0x38800000)
   the space 12288K,   1% used [0x34800000, 0x3482dc68, 0x3482de00, 0x35400000)
    ro space 10240K,  44% used [0x38800000, 0x38c7c1d8, 0x38c7c200, 0x39200000)
    rw space 12288K,  52% used [0x39200000, 0x398440c0, 0x39844200, 0x39e00000)

從上圖看到,如果v=7時,將a的值調整到太大,將會發生GC(不會向survivor區存放),a的值調整到太小(例如new byte[100*1024],如下),物件仍然是存放在Eden區,不會向survivor區域存放。

package com.xtli.jvm;
public class TestOutOfMemory {
     
     public static void main(String[] args) {
          int v = 0;
          while(v<8) {
              if(v==7) {
                   byte[] a = new byte[100*1024];
              } else {
                   byte[] a = new byte[1*1024*1024];
              }
              System.out.println(v++);
          }
     }
}
0
1
2
3
4
5
6
7
Heap
 def new generation   total 9216K, used 8192K [0x33400000, 0x33e00000, 0x33e00000)
  eden space 8192K, 100% used [0x33400000, 0x33c00000, 0x33c00000)
  from space 1024K,   0% used [0x33c00000, 0x33c00000, 0x33d00000)
  to   space 1024K,   0% used [0x33d00000, 0x33d00000, 0x33e00000)
 tenured generation   total 10240K, used 0K [0x33e00000, 0x34800000, 0x34800000)
   the space 10240K,   0% used [0x33e00000, 0x33e00000, 0x33e00200, 0x34800000)
 compacting perm gen  total 12288K, used 183K [0x34800000, 0x35400000, 0x38800000)
   the space 12288K,   1% used [0x34800000, 0x3482dc68, 0x3482de00, 0x35400000)
    ro space 10240K,  44% used [0x38800000, 0x38c7c1d8, 0x38c7c200, 0x39200000)
    rw space 12288K,  52% used [0x39200000, 0x398440c0, 0x39844200, 0x39e00000)

(5)情況5:新建物件時,Eden區記憶體不夠,觸發GC。GC過程中,存活的物件大於survivor記憶體大小,則將存活的物件直接存入老年代區域。

package com.xtli.jvm;
public class TestOutOfMemory {
     
     public static void main(String[] args) {
          int v = 0;
          while(v<1) {
              byte[] a = new byte[1*1024*1024];
              System.out.println(v++);
              byte[] b = new byte[1*1024*1024];
              System.out.println(v++);
              byte[] c = new byte[1*1024*1024];
              System.out.println(v++);
              byte[] d = new byte[1*1024*1024];
              System.out.println(v++);
              byte[] e = new byte[1*1024*1024];
              System.out.println(v++);
              byte[] f = new byte[1*1024*1024];
              System.out.println(v++);
              byte[] g = new byte[1*1024*1024];
              System.out.println(v++);
              //======由前面的例項可知,上面7次建立不會發生GC=====下面的建立將會發生GC=====
              byte[] h = new byte[1*1024*1024];
              System.out.println(v++);
          }
     }
}

結果如下

0
1
2
3
4
5
6
[GC[DefNew: 8044K->431K(9216K), 0.0047010 secs] 8044K->7599K(19456K), 0.0047616 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
7
Heap
 def new generation   total 9216K, used 1709K [0x33400000, 0x33e00000, 0x33e00000)
  eden space 8192K,  15% used [0x33400000, 0x3353f960, 0x33c00000)
  from space 1024K,  42% used [0x33d00000, 0x33d6bd20, 0x33e00000)
  to   space 1024K,   0% used [0x33c00000, 0x33c00000, 0x33d00000)
 tenured generation   total 10240K, used 7168K [0x33e00000, 0x34800000, 0x34800000)
   the space 10240K,  70% used [0x33e00000, 0x34500070, 0x34500200, 0x34800000)
 compacting perm gen  total 12288K, used 183K [0x34800000, 0x35400000, 0x38800000)
   the space 12288K,   1% used [0x34800000, 0x3482dd58, 0x3482de00, 0x35400000)
    ro space 10240K,  44% used [0x38800000, 0x38c7c1d8, 0x38c7c200, 0x39200000)
    rw space 12288K,  52% used [0x39200000, 0x398440c0, 0x39844200, 0x39e00000)

      第8次建立前,發生了GC,新生代記憶體由8044K降到431K(GC後存活的物件放到了survivor區域,從之後的from space可以看出 1024k*42% = 431k),堆記憶體由8044K降到7599K(新生代的431k+老年代7168k),即新生代中儲存的物件放到了老年代區域。

(6)情況6:物件放入老年代時,記憶體空間不夠,發生FullGC。

       設定a物件大小為10*1024*1024

package com.xtli.jvm;
public class TestOutOfMemory {
    
     public static void main(String[] args) {
          int v = 0;
          while(v<1) {
              byte[] a = new byte[10*1024*1024];
              //System.out.println(v++);
              v++;
          }
     }
}

結果如下

[GC[DefNew: 876K->430K(9216K), 0.0010839 secs][Tenured: 0K->429K(10240K), 0.0027068 secs] 876K->429K(19456K), [Perm : 182K->182K(12288K)], 0.0038749 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC[Tenured: 429K->419K(10240K), 0.0022318 secs] 429K->419K(19456K), [Perm : 182K->182K(12288K)], 0.0022807 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
java.lang.OutOfMemoryError: Java heap space
Dumping heap to d:\dump ...
Unable to create d:\dump: File exists
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
     at com.xtli.jvm.TestOutOfMemory.main(TestOutOfMemory.java:7)
Heap
 def new generation   total 9216K, used 409K [0x33400000, 0x33e00000, 0x33e00000)
  eden space 8192K,   5% used [0x33400000, 0x33466750, 0x33c00000)
  from space 1024K,   0% used [0x33d00000, 0x33d00000, 0x33e00000)
  to   space 1024K,   0% used [0x33c00000, 0x33c00000, 0x33d00000)
 tenured generation   total 10240K, used 419K [0x33e00000, 0x34800000, 0x34800000)
   the space 10240K,   4% used [0x33e00000, 0x33e68db0, 0x33e68e00, 0x34800000)
 compacting perm gen  total 12288K, used 185K [0x34800000, 0x35400000, 0x38800000)
   the space 12288K,   1% used [0x34800000, 0x3482e4c0, 0x3482e600, 0x35400000)
    ro space 10240K,  44% used [0x38800000, 0x38c7c1d8, 0x38c7c200, 0x39200000)
    rw space 12288K,  52% used [0x39200000, 0x398440c0, 0x39844200, 0x39e00000)

放入老年代的物件太大,發生了Full GC(一般Full GC之前會伴有一次GC,由於物件太大,直接OOM了)