内容参考自《深入理解java虚拟机》

虚拟机的模型图

java vm虚拟机图片 java虚拟机模型_java vm虚拟机图片

程序计数器

  • 程序计数器是一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器。
  • 字节码解释器工作就是通过改变这个计数器的值来选取下一条需要执行的字节码的指令,例如分支,循环,跳转,异常处理,线程恢复等功能。
  • 每条线程都有一个独立的程序计数器,各条线程之间计数器互相不影响,独立存储。是“线程私有”的内存。

如果线程在执行java方法,计数器记录的是正在执行的虚拟机字节码指令地址;如果是执行native方法,计数器返回空。

  • 此内存区域是java虚拟机唯一没有定义任何oom情况的区域

java虚拟机栈

  • 线程私有内存,生命周期与线程相同。为虚拟机执行的java方法服务
  • 每个方法执行时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
  • 局部变量表中存放基本数据类型(byte、boolean、char、short、int、double、float)和对象的引用类型
  • 线程请求的栈深度大于虚拟机允许的深度,会抛出stackOverflowError;

递归调用方法,以达到栈的深度。



public class JavaVMStackSOF {
    private int stackLength=1;
    public void stackLeak(){
        stackLength++;
        this.stackLeak();
    }
    public static void main(String[] args)throws Throwable {
        JavaVMStackSOF stackSOF = new JavaVMStackSOF();
        try {
            stackSOF.stackLeak();
        } catch (Throwable e) {
            System.out.println("stackSOF = " + stackSOF.stackLength);
            throw e;
        }
    }
}
  • 扩展时如果无法申请到足够内存,会抛出OutOfMemoryError

不断创建多线程来申请栈内存



public class JavaVMStackOOM {

    private void dontStop(){
        while (true){

        }
    }

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

    //这里要抛Throwable
    public static void main(String[] args)throws Throwable {
        JavaVMStackOOM stackOOM = new JavaVMStackOOM();
        stackOOM.stackLeakByThread();
    }
}

本地方法栈

  • 线程私有内存,生命周期与线程相同。为虚拟机执行的native方法服务
  • 线程请求的栈深度大于虚拟机允许的深度,会抛出stackOverflowError;
  • 扩展时如果无法申请到足够内存,会抛出OutOfMemoryError

java堆

  • 所有线程共享的内存区域,虚拟机启动时创建。是jvm中内存最大的一块。
  • 用于存放所有的对象实例和数组。
  • 如果堆中没有内存来完成实例分配,会抛出OutOfMemoryError.


/**
 * -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
 * @description:jvm堆内存的oom
 * 不断创建对象。
 */
public class JavaVmHeapOOM {
    static class OOMObject{

    }

    public static void main(String[] args)throws Throwable {
        List<OOMObject> list = new ArrayList<>();
        while (true){
            list.add(new OOMObject());
        }
    }
}

方法区

  • 线程共享的内存区域
  • 存储被虚拟机加载的类信息、变量、静态变量等
  • 方法区无法满足内存分配时,抛出OOM。


/**
 * @description:java方法区栈溢出
 * 1.8没有permSize(永久代)使用metaspace(元空间)来取代
 * -XX:MaxMetaspaceSize=2m
 */
public class JavaMethodAreaOOM {
    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.invokeSuper(o,objects);
                }
            });
            enhancer.create();
        }
    }
    static class OOMObject{

    }
}
/**
 * @description:java方法区栈溢出
 * 1.8没有permSize(永久代)使用metaspace(元空间)来取代
 * -XX:MaxMetaspaceSize=2m
 */
public class JavaMethodAreaOOM {
    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.invokeSuper(o,objects);
                }
            });
            enhancer.create();
        }
    }
    static class OOMObject{

    }
}

参考博客:
http://caoyaojun1988-163-com.iteye.com/blog/1969853