深入JVM-理解内存溢出

本文通过几个实例来验证JVM运行时数据区发生OutOfMemoryError(OOM)异常的场景,顺便介绍几个内存相关的基本的虚拟机参数。

1.堆内存溢出

1.1.相关参数

  • -Xms 堆的最小值
  • -Xmx 堆的最大值(Xms和Xmx设置一致,可以避免堆自动扩展)
  • -Xmn 堆中新生代大小
  • -XX:+HeapDumpOnOutOfMemoryError 可以在内存溢出时,dump出当前内存堆转储为快照文件以供事后进行分析
  • -XX:+PrintGCDetails 在启动脚本可以自动开启-XX:+PrintGC , 如果在命令行使用jinfo开启的话,不会自动开启-XX:+PrintGC
  • -XX:SurvivorRatio 设置Eden和Survivor区域大小比例,如果设置为8,则Eden和两个Survivor区比例为8:1:1

1.2.测试代码

package com.glt.oom;
import java.util.ArrayList;
import java.util.List;

/**
 * JVM args:
 *      -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError
 */
public class HeapOOM {

    static class OOMObject{

    }

    public static void main(String[] args) {

        List<OOMObject> list = new ArrayList<OOMObject>();

        while (true){
            list.add(new OOMObject());
        }
    }
}

输入如下:
深入JVM-理解内存溢出_第1张图片

2.虚拟机栈和本地方法栈溢出

栈溢出存在两种情况:

  • SOF异常(StackOverflowError)
    线程请求的栈深度大于虚拟机所允许的最大深度,则抛出此异常
  • OOM异常(OutOfMemoryError)
    虚拟机在扩展栈时无法申请到足够的内存空间,则抛出此异常

2.1.相关参数

  • -Xss 栈容量

2.2.测试栈SOF异常

package com.glt.oom;

/**
 * JVM args:
 * -Xss128k  -XX:+PrintGCDetails
 */
public class StackSOF {

    private int stackLen = 1;

    public void stackLeak() {
        stackLen++;
        stackLeak();
    }

    public static void main(String[] args) throws Exception{
        StackSOF stackSOF = new StackSOF();
        try {
            stackSOF.stackLeak();
        } catch (Exception e) {
            System.out.println("stack 深度为:" + stackSOF.stackLen);
            e.printStackTrace();

            throw e;
        }
    }
}

输出如下:
深入JVM-理解内存溢出_第2张图片
深入JVM-理解内存溢出_第3张图片

2.3.测试栈OOM异常

此处会造成windows系统假死,请谨慎操作!!!

package com.glt.oom;

/**
 *  JVM args:
 *      -Xss2M  -XX:+PrintGCDetails
 */
public class StackOOM {

    private void dontStop(){
        while (true){

        }
    }

    public void stackLeakByThread(){
        while (true){
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    dontStop();
                }
            });
            thread.start();
        }
    }

    public static void main(String[] args) {
        StackOOM stackOOM = new StackOOM();
        stackOOM.stackLeakByThread();
    }
}

3.方法区溢出

3.1.相关参数

  • -XX:PermSize 非堆(方法区)区域初始内存大小
  • -XX:MaxPermSize 非堆(方法区)区域分配的最大内存

3.2.测试方法区内存溢出(OOM)

方法区用于存放class类信息,如类名,访问修饰符,常量池,字段描述,方法描述等,测试溢出需要使用大量类填满方法区,此处使用cgLib动态创建大量类测试。

package com.glt.oom;


import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * JVM args
 * -XX:PermSize=10M -XX:MaxPermSize=10M -XX:+PrintGCDetails
 */
public class MethodAreaOOM {

    public static void main(String[] args) {
        while (true) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(ObjectOOM.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    return methodProxy.invokeSuper(o, objects);
                }
            });
            enhancer.create();
        }
    }

    static class ObjectOOM {
    }
}

输出如下:
深入JVM-理解内存溢出_第4张图片

3.3.测试运行时常量池溢出(OOM)

通过限制方法区大小来限制运行时常量池大小

package com.glt.oom;

import java.util.ArrayList;
import java.util.List;

/**
 * JVM args:
 *  -XX:PermSize=10M -XX:MaxPermSize=10M -XX:+PrintGCDetails
 */
public class RuntimeConstPoolOOM {

    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();

        int i = 0;
        while (true) {
            list.add(String.valueOf(i++).intern());
        }
    }
}

输出如下:
深入JVM-理解内存溢出_第5张图片

4.直接内存溢出

4.1.相关参数

  • -XX:MaxDirectMemorySize 来指定最大直接内存

4.2.测试直接内存溢出(OOM)

直接内存不是运行时数据区部分,但这部分内存也被频繁的使用,而且也会导致OutOfMemoryError异常的出现。

package com.glt.oom;

import sun.misc.Unsafe;

import java.lang.reflect.Field;

/**
 * JVM args
 *      -Xmx20M -XX:MaxDirectMemorySize=10M -XX:+PrintGCDetails
 */
public class DirectMemoryOOM {

    private static final int _1M = 1024 * 1024;

    public static void main(String[] args) throws Exception {
        Field unsafeField = Unsafe.class.getDeclaredFields()[0];
        unsafeField.setAccessible(true);
        Unsafe unsafe = (Unsafe) unsafeField.get(null);
        while (true) {
            unsafe.allocateMemory(_1M);
        }
    }
}

输出如下:
深入JVM-理解内存溢出_第6张图片

你可能感兴趣的:(深入理解JVM)