运行时数据区
java虚拟机在执行java程序的过程中会把所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途,以及创建和销毁的时间。
- 有的区域随着虚拟机进程的启动而存在,随虚拟机进程的退出而销毁;
- 有的区域则依赖用户线程的启动和结束而建立和销毁。
java虚拟机所管理的内存将会包括以下几个运行时数据区域
程序计数器
程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。
由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。因此,为了切换线程后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,称这块区域为“线程私有”的内存。
如果线程正在执行的是一个java方法,这个程序计数器记录正在执行的虚拟机字节码指令的地址;如果正在执行的是Native方法,这个程序计数器的值为空(Undefined)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
虚拟机栈
- 线程私有,生命周期与线程相同。
虚拟机栈描述的是java方法执行的内存模型:每次方法调用都会创建一个帧栈,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个帧栈在虚拟机栈中入栈到出栈的过程。
JVM Stack 异常情况:
StackOverflowError:当线程请求分配的栈容量超过JVM允许的最大容量时抛出OutOfMemoryError:如果JVM Stack可以动态扩展,但是在尝试扩展时无法申请到足够的内存去完成扩展,或者在建立新的线程时没有足够的内存去创建对应的虚拟机栈时抛出。
本地方法栈
为虚拟机执行Native方法服务
和虚拟机栈一样,同样会抛出StackOverflowError和OutOfMemoryError异常
Java堆
线程共享
存放对象实例
虚拟机启动时创建
Java堆的容量可以是固定大小,也可以随着需求动态扩展,并在不需要过多空间时自动收缩。
Java堆所使用的内存不需要保证是物理连续的,只要逻辑上是连续的即可。
如果在队中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常
方法区
线程共享
用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
虚拟机启动时创建
JDK6时,String等常量信息置于方法区
JDK7时,已经移动到了堆该区域的内存回收比较少出现,主要是针对常量池的回收和类型的卸载
运行时常量池
运行时常量池(Runtime Constant Pool)是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池。用于存放编译期产生的各种字面常量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。
既然运行时常量池是方法区的一部分,自然会受到方法区的限制,当常量池无法再申请到内存时会抛出OutOfMemeoryError异常。