jvm測試(記憶體分配、回收、-XX:+PrintCompilation)
一、記憶體分配與
1、OutOfMemoryError (堆異常)
Eclipse:
VM argument:
(1)-verbose:gc:顯示虛擬機器記憶體回收資訊。
[GC (Allocation Failure) 952K->767K(59392K), 0.0073992 secs]
有952-767=185k的記憶體被回收。java堆大小為59392k。
(2)
-Xms20M //堆最小值為20MB
-Xmx20M //堆最大值20MB
-Xmn20M //儲存新生成物件的空間
-XX:SurvivorRatio=4 //年輕代中Eden區與Survivor區的大小比值
/*VM arguments:
-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
*/
package com.hi;
import java.util.*;
public class myjava {
public static void main(String[] args) {
// TODO Auto-generated method stub
List<myjava> list=new ArrayList<myjava>();
while (true){
list.add(new myjava());
}
}
} //丟擲堆記憶體異常(java.lang.OutOfMemoryError: Java heap space)
2、虛擬機器棧和方法棧異常
若執行緒請求的棧深度大於jvm允許的最大深度,丟擲StackOverflowError
若jvm在擴充套件棧時,無法申請足夠記憶體,丟擲OutOfMemoryError
//vm arguments : -Xss128k (棧大小)
import java.util.*;
public class myjava {
private int size=1;
public void stackleak(){
size++;
stackleak();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
myjava com=new myjava();
com.stackleak();
}
} //java.lang.StackOverflowError
作業系統為每個程序分配的最大記憶體為2GB,對於jvm程序,若分配給棧的容量越大,可建立的新的執行緒就越少。建立新的執行緒容易把記憶體耗盡。
例如:
while (true){
Thread trd=new Thread(new Runnable(){public void run(){fun();}});
} //OutOfMemoryError
3、執行時常量池溢位:
package com.hi;
import java.util.*;
public class myjava {
public static void main(String[] args) {
List<String> list=new ArrayList<String>();
int i=0;
while(true){
list.add(String.valueOf(i++).intern());
} //intern() :若常量池沒有當前的這個物件,則將此對相關加入常量池。
}
} //OutOfMemoryError (RuntimeConstantPoolOOM)
4、方法區溢位:
方法區:類資訊(類名、修飾符、常量池、方法描述等)
使用cglib不斷擴充類 ,使方法區記憶體溢位。//OutOfMemory
5、直接記憶體溢位:
VM arguments:
-Xmx20M -XX:MaxDirectMemorySize=10M
package com.hi;
import java.lang.reflect.Field;
import sun.misc.Unsafe; //Unsafe 類用於分配記憶體
public class myjava {
private static final int i=1024*1024;
public static void main(String[] args) throws Exception{
Field unsafeField=Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
Unsafe us=(Unsafe)unsafeField.get(null);
while(true){
us.allocateMemory(i);
}
}
}
執行結果:
Exception in thread “main” java.lang.OutOfMemoryError
at sun.misc.Unsafe.allocateMemory(Native Method)
at com.hi.myjava.main(myjava.java:15)
6、記憶體分配與回收:
Minor GC:新生代回收
Major GC/ Full GC :老年代回收
物件(小物件)優先在Eden分配。大物件直接分配到老年代。???
package com.hi;
public class myjava {
public static final int _1MB=1024*1024;
public static void main(String[] args){
byte[] a1,a2,a3,a4;
a1=new byte[2*_1MB];
a2=new byte[2*_1MB];
a3=new byte[2*_1MB];
a4=new byte[3*_1MB]; //3MB被分配在Eden,但是4MB就被直接分配到OldGen??(並不是Eden區滿了,才垃圾回收)
}
}
vm:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+PrintGCDetails
輸出:
[GC (Allocation Failure) [PSYoungGen: 7652K->840K(9216K)] 7652K->6992K(19456K), 0.0042060 secs] [Times: user=0.05 sys=0.02, real=0.00 secs]
[Full GC (Ergonomics) [PSYoungGen: 840K->0K(9216K)] [ParOldGen: 6152K->6862K(10240K)] 6992K->6862K(19456K), [Metaspace: 2751K->2751K(1056768K)], 0.0085179 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Heap
PSYoungGen total 9216K, used 3318K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 40% used [0x00000000ff600000,0x00000000ff93d8f0,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen total 10240K, used 6862K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 67% used [0x00000000fec00000,0x00000000ff2b3a08,0x00000000ff600000)
Metaspace used 2760K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 305K, capacity 386K, committed 512K, reserved 1048576K
package com.hi;
public class myjava {
public static final int _1MB=1024*1024;
public static void main(String[] args){
byte[] a1,a2,a3,a4;
a1=new byte[2*_1MB];
a2=new byte[2*_1MB];
a3=new byte[2*_1MB];
a4=new byte[8*_1MB]; //大物件直接分配到oldgen
}
}
//vm:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+PrintGCDetails
//輸出資訊:
Heap
PSYoungGen total 9216K, used 7816K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 95% used [0x00000000ff600000,0x00000000ffda22e8,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
to space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
ParOldGen total 10240K, used 8192K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 80% used [0x00000000fec00000,0x00000000ff400010,0x00000000ff600000)
Metaspace used 2757K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 305K, capacity 386K, committed 512K, reserved 1048576K
長期存活的物件進入老年代:
每個物件設定一個年齡計數器。經歷一次Minor GC並且能被Survivor,年齡加1。若年齡大於15(預設),會被移動到老年代。
-XX:MaxTenuringThreshold=1 ,設定移動時的年齡
動態物件年齡的判定:若Survivor空間中相同年齡的物件的大小之和大於Survivor空間的一般,則大於或等於該年齡的物件就可以進入老年代。無需判定年齡計數器。
空間分配擔保:
vm會判斷即將晉升到老年代的物件的平均大小,若大於老年代剩餘空間,直接Full GC。若小於,要檢視HandlePromotionFailure是否允許擔保失敗,若允許,只會Minor GC,若不允許,直接Full GC。
-XX:+PrintCompilation:輸出編譯資訊
package com.hi;
public class myjava {
public static final int NUM=15000;
public static int doubleValue(int i){
return i*2;
}
public static long calSum(){
long sum=0;
for(int i=0;i<100;i++){
sum+=doubleValue(i);
}
return sum;
}
public static void main(String[] args) throws Exception{
for(int i=0;i<NUM;i++){
calSum();
}
}
}
執行輸出資訊:
180 1 3 java.lang.String::equals (81 bytes) 197 5 n 0 java.lang.System::arraycopy (native) (static) 197 4 4 java.lang.String::length (6 bytes) 198 2 3 java.lang.String::hashCode (55 bytes) 198 3 4 java.lang.String::charAt (29 bytes) 199 6 3 java.util.Arrays::copyOfRange (63 bytes) 200 8 3 java.lang.AbstractStringBuilder::ensureCapacityInternal (16 bytes) 200 9 3 java.lang.Object::<init> (1 bytes) 200 7 3 java.lang.String::<init> (62 bytes) 201 10 3 java.lang.CharacterData::of (120 bytes) 201 11 3 java.lang.CharacterDataLatin1::getProperties (11 bytes) 202 13 3 java.lang.Character::toLowerCase (9 bytes) 202 14 3 java.lang.CharacterDataLatin1::toLowerCase (39 bytes) 202 12 3 java.lang.AbstractStringBuilder::append (29 bytes) 203 15 3 java.lang.StringBuilder::append (8 bytes) 203 16 3 java.io.WinNTFileSystem::isSlash (18 bytes) 204 17 s 3 java.lang.StringBuffer::append (13 bytes) 208 18 3 java.lang.String::getChars (62 bytes) 212 19 3 java.io.BufferedInputStream::getBufIfOpen (21 bytes) 212 20 s 3 java.io.BufferedInputStream::read (49 bytes) 213 21 3 java.io.DataInputStream::readUTF (501 bytes) 216 24 3 java.io.DataInputStream::readFully (63 bytes) 216 25 s 3 java.io.BufferedInputStream::read (113 bytes) 217 27 3 java.io.DataInputStream::readShort (40 bytes) 217 26 3 java.io.BufferedInputStream::read1 (108 bytes) 218 22 3 java.io.DataInputStream::readUTF (5 bytes) 218 23 3 java.io.DataInputStream::readUnsignedShort (39 bytes) 219 28 3 java.util.HashMap::hash (20 bytes) 223 29 3 java.lang.String::indexOf (70 bytes) 229 30 3 java.lang.Math::max (11 bytes) 231 31 3 java.lang.AbstractStringBuilder::append (50 bytes) 232 32 3 java.lang.StringBuilder::append (8 bytes) 261 33 1 java.lang.Object::<init> (1 bytes) 262 9 3 java.lang.Object::<init> (1 bytes) made not entrant 262 34 3 java.lang.Math::min (11 bytes) 264 35 3 com.hi.myjava::doubleValue (4 bytes) 264 36 1 com.hi.myjava::doubleValue (4 bytes) 264 35 3 com.hi.myjava::doubleValue (4 bytes) made not entrant 264 37 3 com.hi.myjava::calSum (26 bytes) 265 38 % 4 com.hi.myjava::calSum @ 7 (26 bytes) 267 39 4 com.hi.myjava::calSum (26 bytes) 269 37 3 com.hi.myjava::calSum (26 bytes) made not entrant
- 第一列:時間戳
- 第二列:編譯ID 。 帶有%說明Back Edge Counter觸發了OSR編譯
OSR:迴圈體所在的方法標識為Hot Spot Code(並非只是迴圈體) - 第三列:編譯等級。1、2、3表示由C1編譯,4由C2編譯,0表示直譯器
- 第四列:編譯的方法名 -