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了)