JVM内存分区和各分区溢出测试

蚩尤后裔 2020-05-31

JVM分区

线程共享区:方法区,堆

方法区

用于存储已经被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码缓存等数据。
方法区!=永久代,只是在永久代这个概念还存在的时候,为了方法区能像堆一样进行分代收集,将方法区采用永久代实现。永久代的概念被抛弃后(JDK8),方法区采用元空间来实现(Meta-space)。
(JDK7之前字符串常量池位于方法区当中,JDK7将字符串常量池移到了堆。)

在虚拟机启动的时候创建,此区域的目的是存放对象实例,几乎所有的对象实例都要在堆中分配内存。

线程隔离区:程序计数器,虚拟机栈,本地方法栈

程序计数器

一块较少的内存区域,可以看作当前线程所执行的字节码的行号指示器,字节码解释器就是通过改变计数器的值来选取下一条需要执行的字节码,所以它是程序控制流的指示器。

虚拟机栈

虚拟机栈是线程私有的,所以它的生命周期和线程生命周期相同,
每个方法执行的时候,虚拟机栈都会创建一个栈帧,用于存放跟该方法执行相关的东西,如局部变量表,操作数栈,动态连接,方法出口等等,此外Hotspot虚拟机栈是不可以动态扩展的。

本地方法栈

和虚拟机的区别在于本地方法栈是为native方法服务的

各区内存溢出

虚拟机栈溢出

/**
 * @author yintianhao
 * @createTime 2020/5/25 9:44
 * @description 栈溢出测试
 */
public class StackOverFlowErrorTest {
    //栈内存分为两种,一种是线程栈内存,一种是JVM栈内存
    //线程栈内存可以通过-Xss设置,执行的方法创建的栈帧存储在当中,当栈内存不够时,抛异常
    //1 线程请求的栈深度大于虚拟机所允许的最大深度,抛出StackOverflowError
    public static void OOMByDigui(){
        OOMByDigui();
    }

    //2 方法中使用了大量的大变量,增大了方法帧中本地变量表的长度
    public static void OOMByBigMethodFrame(){
        long unused1,unused2,unused3,unused4,unused5,
                unused6,unused7,unused8,unused9,unused10,
                unused11,unused12,unused13,unused14,unused15,
                unused16,unused17,unused18,unused19,unused20,
                unused21,unused22,unused23,unused24,unused25,
                unused26,unused27,unused28,unused29,unused30,
                unused31,unused32,unused33,unused34,unused35,
                unused36,unused37,unused38,unused39,unused40,
                unused41,unused42,unused43,unused44,unused45,
                unused46,unused47,unused48,unused49,unused50,
                unused51,unused52,unused53,unused54,unused55,
                unused56,unused57,unused58,unused59,unused60,
                unused61,unused62,unused63,unused64,unused65,
                unused66,unused67,unused68,unused69,unused70,
                unused71,unused72,unused73,unused74,unused75,
                unused76,unused77,unused78,unused79,unused80,
                unused81,unused82,unused83,unused84,unused85,
                unused86,unused87,unused88,unused89,unused90,
                unused91,unused92,unused93,unused94,unused95,
                unused96,unused97,unused98,unused99,unused100;
        OOMByBigMethodFrame();
        unused1 = unused2=unused3=unused4=unused5=
                unused6=unused7=unused8=unused9=unused10=
                unused11=unused12=unused13=unused14=unused15=
                unused16=unused17=unused18=unused19=unused20=
                unused21=unused22=unused23=unused24=unused25=
                unused26=unused27=unused28=unused29=unused30=
                unused31=unused32=unused33=unused34=unused35=
                unused36=unused37=unused38=unused39=unused40=
                unused41=unused42=unused43=unused44=unused45=
                unused46=unused47=unused48=unused49=unused50=
                unused51=unused52=unused53=unused54=unused55=
                unused56=unused57=unused58=unused59=unused60=
                unused61=unused62=unused63=unused64=unused65=
                unused66=unused67=unused68=unused69=unused70=
                unused71=unused72=unused73=unused74=unused75=
                unused76=unused77=unused78=unused79=unused80=
                unused81=unused82=unused83=unused84=unused85=
                unused86=unused87=unused88=unused89=unused90=
                unused91=unused92=unused93=unused94=unused95=
                unused96=unused97=unused98=unused99=unused100=0;

    }

    //JVM栈内存通常指的是计算机的本地内存,所以此时一般JVM栈内存溢出一般是方法中申请线程过多导致。
    //3 虚拟机在扩展栈深度时无法申请到足够的内存空间,抛出OutOfMemoryError
    // -Xss2M
    public static void OOMByMuchThread(){
        while (true){
            Runnable r = new Runnable() {
                @Override
                public void run() {
                    dontStop();
                }
            };
            Thread t = new Thread(r);
            t.start();
        }
    }
    private static void dontStop(){
        while(true){

        }
    }

    public static void main(String[] args){
        //OOMByDigui();
        //OOMByBigMethodFrame();
        OOMByMuchThread();
    }
}

方法区溢出

/**
 * @author yintianhao
 * @createTime 2020/5/29 16:55
 * @description
 * @VMArgs -XX:MaxMetaspaceSize=10m
 */
public class MethodAreaOOMTest {

    //JDK 1.8当中将方法区用本地内存实现,因此无法再通过-XX:PermSize来设置方法区大小限制
    //但是可以通过-XX:MaxMetaspaceSize=10m设置元空间的大小来模拟溢出
    public static void main(String[] args){
        while (true){
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(OOMObject.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    return methodProxy.invoke(o,objects);
                }
            });
            enhancer.create();
            //System.out.println("--");
        }
    }
    static class OOMObject{}

}

堆溢出

/**
 * @author yintianhao
 * @createTime 2020/5/25 11:14
 * @description OOM Test
 * @Options -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
 */
public class HeapOutOfMemoryTest {

    //堆空间主要存储对象,不断new对象会导致内存溢出
    public static  void main(String[] args){
        List<OOMObject> list = new ArrayList<>();
        while (true){
            list.add(new OOMObject());
        }
    }
    static class OOMObject{
        private char[] content = new char[1000];
    }
}

常量值溢出

/**
 * @author yintianhao
 * @createTime 2020/5/25 17:36
 * @description 常量池内存溢出测试
 */
public class ConstantPoolOOMTest {

    public static void main(String[] args){
        Set<String> set = new HashSet<>();
        while (true){
            set.add(UUID.randomUUID().toString().intern());
        }
    }
}

直接内存溢出

/**
 * @author yintianhao
 * @createTime 2020/5/30 0:05
 * @description 直接内存溢出
 * @VMargs -Xmx20M -XX:MaxDirectMemorySize=10M 如果没有指定,那么和java堆最大值的大小一致
 */
public class DirectMemoryOOMTest {

    private static final int _1MB = 1024 * 1024;

    public static void main(String[] args) throws IllegalAccessException{
        //越过DirectByteBuffer直接获取Unsafe实例进行内存分配
        Field unsafeField = Unsafe.class.getDeclaredFields()[0];
        unsafeField.setAccessible(true);
        Unsafe unsafe = (Unsafe)unsafeField.get(null);
        while(true){
            unsafe.allocateMemory(_1MB);
        }
    }
}

同步博文: https://izzer.cn/archives/20200531

相关推荐