运行内存区域
程序计数器
当前线程所执行的字节码的行号指示器,字节码解释器就是通过改变该计数器来选取下一个要执行的字节码指令。
多线程的情况下线程切换后回到正确的执行位置需要该计数器。
线程私有。该区域不会存在OOM情况。
虚拟机栈
线程私有。描述方法执行时线程的内存模型,每个方法被执行时,虚拟机会创建一个栈帧用于存放局部变量表、方法出口等信息。
方法被调用到执行完毕,对应一个栈帧在虚拟机栈中从入栈到出栈的过程。
常规的“栈”指的是虚拟机栈,更多指的是局部变量表,局部变量表存放编译器可知的基本数据类型、对象引用等。
如果线程请求的栈深度大于虚拟机允许的深度,会抛出StackOverflowError;如果线程申请栈空间申请失败时会报OOM。
本地方法栈
本地方法栈为虚拟机使用到的本地方法(Native)服务(虚拟机栈为虚拟机执行Java方法,即执行字节码服务)。
本地方法栈也会在栈深度溢出或栈内存申请失败也会报StackOverflowError或OOM。
Java堆
用于存放对象实例,虚拟机规范对堆的描述是:所有的对象实例以及数组都应在堆上分配。
但随着逃逸分析技术的发展,栈上分配、标量替换等优化手段使对象实例都分配在堆上已不绝对。
传统的经典分代:新生代(1Een+2Survivor)和老年代是Hotspot采用的方式。
Java7之后 字符串常量池在堆中,类的静态变量也转移到了堆中(本身在堆,但引用是元数据的一部分,在Metaspace)。
Java堆一旦没有内存完成实例分配,则会报OOM。
方法区
各线程共享,主要存放被加载的类型信息等。
永久代只是 Hotspot 中的概念,且在 JDK8 之后也被废弃,现使用在本地内存中实现的元空间来代替,类型信息由元空间进行管理。
虚拟机规范规定,如果方法区无法满足新的内存分配需求,将抛出OOM。
运行时常量池
是方法区的一部分,存放编译器生成的各种字面量和符号引用。
运行时常量池具备动态性,新常量也可以进入池中。