jvm测试(内存分配、回收)

一、内存分配与
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。

猜你喜欢

转载自blog.csdn.net/misterfm/article/details/80680732