运行时数据区域
在Java程序运行时,Java虚拟机将其管理的内存划分为不同的运行时数据区域:
程序计数器
如果对计算机组成有所了解的话,应该很容易理解程序计数器(PC),可以认为其中的数据就是下一条指令的地址(字节码行号),在当前指令执行完毕后,JVM会根据PC中的数据找到下一条指令。除此以外,在当前线程被中断后恢复时,JVM也要根据PC中的值恢复现场。该内存区域并没有规定任何OOM的情况。
虚拟机栈
在一个线程中,每当开始执行一个普通的Java方法时,虚拟机会为该线程创建一个栈帧(Stack Frame),并将其放入该线程所属的虚拟机栈中。当该方法执行完成后,对应的栈帧就会出栈。
栈帧中保存了方法的局部变量表、操作数栈、动态连接、方法出口等信息。
- 局部变量表:存放方法参数和方法内部定义的局部变量。该表的容量以变量槽(Variable Slot)为单位,一个变量槽能够存放一个boolean、btye、char、short、int、float、reference或returnAddress类型的数据,而一个变量槽的实际内存大小取决于处理器、操作系统和虚拟机的实现。
- 操作数栈:用于执行过程中保存操作数和结果。如做算数运算时存放操作数,或者在调用其他方法时存放参数。
- 动态连接:运行时常量池中有一部分方法的引用在运行时才转化为直接引用,这部分的引用就称为动态连接。、
- 方法出口:也叫方法返回地址。即该方法在执行完毕后,下一条应该执行的指令的地址。
该区域有两种可能的异常状况:
- 如果线程请求的栈深度大于虚拟机所允许的深度,抛出StackOverFlow异常
- 如果虚拟机栈容量可以动态扩展,当栈扩展时无法申请到足够的内存会抛出OutOfMemeoryError异常
本地方法栈
本地方法栈的作用与虚拟机栈很相似,区别在于当执行的方法是本地方法时,栈帧会被压入本地方法栈。甚至有的虚拟机在实现时直接将其与虚拟机栈合并。
Java堆
Java堆用来存放几乎所有的对象实例,所有线程共享这一区域。垃圾收集器(Garbage Collector)管理的就是Java堆中的对象。
Java堆可以被实现为固定大小,也可以是可扩展的。如果Java堆的空间已不够用,并且也无法扩展时,JVM会抛出OutOfMemoryError异常。
方法区
方法区用于存储被虚拟机加载的类型信息、常量、静态变量、即时编译器变异后的代码缓存等数据,所有线程共享这一区域。
其中,类型信息包含:类的元信息(类的完整名称,修饰符,父类,接口等),类型的常量池,字段信息,方法信息,类变量,指向加载器的引用,指向Class实例的引用等。
如果方法区无法满足新的内存分配需求时,将抛出OutOfMemoryError异常。
运行时常量池
运行时常量池是方法区的一部分,用于存放编译时期产生的各种字面量(常量)与符号引用。这些常量在编译时期由编译器生成在Class文件中的常量池表中,JVM在运行时将其复制到运行时常量池中。
总结
数据区域 | 存放内容 | 产生时期/销毁时期 | 可扩展 | 异常状况 |
---|---|---|---|---|
程序计数器 | 下一条指令“地址” | 线程生成/线程销毁 | 不可扩展 | 无 |
虚拟机栈 | 栈帧 | 线程生成/线程销毁 | 允许扩展 | StackOverflowError, OutOfMemoryError |
Java堆 | 对象实例 | 虚拟机启动/虚拟机关闭 | 允许扩展 | OutOfMemoryError |
方法区(包括运行时常量池) | 已加载的类信息,静态变量等(常量保存在运行时常量池中) | 虚拟机启动/虚拟机关闭 | 允许扩展 | OutOfMemoryError |