JVM各区域的用途

程序计数器

用于给字节码解释器来选取吓一跳需要执行的字节码指令。每个线程有一个独立的程序计数器去,且各个线程之间互不影响。如果线程正在执行一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的内存地址;如果执行的是Native方法。在计数器为Undefined。此区域是JVM规范中唯一一个不存在OOM(内存溢出)的区域。


虚拟机栈(局部变量空间)

存放编译器可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象应用(reference)。64位的double、long占用2个槽。内存空间在编译期间就能确定,当进入一个方式时,这个方法需要分配的局部变量空间是完全确定的,通过-Xss设置内存容量。
异常状况:
- *Error栈深度大于虚拟机所允许的深。
- OOM 如果虚拟机栈可以动态扩展(当前大部分Java虚拟机都可以动态扩展,只不过Java虚拟机规范中的也允许固定长度的虚拟机栈),如果扩展是无法申请到足够的内存。


本地方法栈

跟虚拟机栈类似,只是一个是虚拟机执行Java方法,一个是执行Native方法

异常状况:

  • *Error 栈深度大于虚拟机所允许的深度
  • OOM(内存溢出)

Java堆

线程共享的一块内存区域,从内存回收角度来看,基本都采用分代收集算法,所以分为新生代、老年代。再细致一点可以分为Eden空间、From Survivor空间、To Survivor空间等。-Xmx-Xms控制堆空间大小。

异常状况:
- OOM(内存耗尽) 堆无法扩展时。

/**
 * VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
 * 
 * java.lang.OutOfMemoryError: Java heap space
 */
public class HeapOOM {

    static class OOMObject {
    }

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

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

方法区

线程间共享。存储已经被虚拟机加载的类信息、常量、静态变量、即时编辑器编译后的代码等数据,在HotSpot虚拟机中可以称为永生代。运行时常量也是方法区的一部分(String.intern()动态加入常量池)-XX:MaxPermSize控制大小。

异常状况:
- OOM

/**
* VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M
*
* java.lang.OutOfMemoryError:PermGen space
*/
public class RuntimeConstantPoolOOM {

public static void main(String[] args) {
    // 使用List保持着常量池引用,避免Full GC回收常量池行为
    List<String> list = new ArrayList<String>();
    // 10MB的PermSize在integer范围内足够产生OOM了
    int i = 0;
    while (true) {
        list.add(String.valueOf(i++).intern());
    }
}
}

直接内存(不属于虚拟机运行时的数据区的一部分)

NIO可以使用Native函数库直接分配对外的内存,然后通过存储在Java对中的DirectByteBuffer对象作为这块内存的引用进行操作。受限于机器物理内存,可以通过-XX:MaxDirectMemorySize制定,如果不制定,默认与Java堆最大值(-Xmx)一样。
异常状况:
- OOM

public class DirectMemoryOOM {

    private static final int _1MB = 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(_1MB);
        }
    }
}
上一篇:业务与信息系统深度融合,改变业务运营和创新模式:阿里巴巴企业级互联网架构(Aliware)实践


下一篇:linux基本命令学习02