运行时数据区
程序计数器
在虚拟机规范模型里,字节码解释器通过改变计数器的值来选取下一条需要执行的字节码指令。分支、循环、跳转、异常处理、线程恢复等基础功能依赖计数器来完成。
在一个确定时刻,一个处理器只会执行一条线程中的指令。
线程私有,jvm中规范中没有规定任何OutOfMemoryError情况的区域。
虚拟机栈
线程私有,生命周期与线程相同。虚拟机栈描述的是java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直到执行完成的过程,对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
局部变量表存放了基本数据类型、对象引用(reference)和rerurnAddress类型(指向了一条字节码指令的地址)。其中64位长度的long和double类型的数据占用2个局部变量空间,其余的数据类型占用1个。
方法需要在帧中分配多大的局部变量空间是确定的,在方法运行期间不会改变局部变量表的大小。
有两种异常情况:
StackOverflowError:线程请求的栈深度大于虚拟机所允许的深度。
OutOfMemoryError:虚拟机栈扩展时无法申请到足够的内存。
本地方法栈
线程私有,为虚拟机使用到的Native方法服务。
虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构没有强制规定。有的虚拟机(如 Sun HotSpot虚拟机)把本地方法栈和虚拟机栈合二为一。
有两种异常情况:StackOverflowError,OutOfMemoryError。
堆
java堆被所以线程共享,在虚拟机启动时创建。几乎所有的对象实例都在这里分配,但是随着JIT编译技术的发展与逃逸分析技术的成熟,栈上分配,标量替换优化技术会导致所有对象都分配在堆上不那么绝对。
堆是垃圾收集管理的主要区域,也被称作“GC堆”。如果从内存回收的角度看,由于现在收集器基本都是采用分代收集算法,所以java堆可以细分为:新生代和老年代;再细致一点的有Eden空间、From Survivor空间、ToSurvivor空间等。如果从内存分配的角度看,可以划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB)。
java虚拟机规范的规定,堆可以处于物理上不连续的内存空间,只要逻辑上连续即可。当前主流的虚拟机都是按照可扩展实现的(通过 -Xmx 和 -Xms控制)。
异常: OutOfMemoryError:如果堆中没有内存完成实例分配,并且堆也无法再扩展时,抛出异常。
方法区
方法区是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器后的代码等数据。
方法区和堆一样不需要连续的内存空间和可以选择固定大小或者可扩展外,还可以选择不实现垃圾收集。
异常:OutOfMemory Error:当方法区无法满足内存分配需求时。
运行时常量池
运行时常量池(Runtime Constant Pool)是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等信息描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
Java虚拟机对Class文件的每一部分的格式都有严格的规定。但对于运行时常量池,没有做任何细节的要求,不同提供商实现的虚拟机可以按照需要来实现这个内存区域。除了保存Class文件中描述的符号引用外,还会把翻译出来的直接引用也存储在运行时常量池。
运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只能在编译时期产生,运行期间也可以将新的常量放入运行时常量池中。
异常:OutOfMemoryError:当常量池无法申请到内存时。
程序计数器 | 私有 | 通过改变计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能依赖计数器来完成。 | |
虚拟机栈 | 私有 | StackOverflowError,OutOfMemoryError | 每个方法被执行的时候都会同时创建一个栈帧用于存储局部变量表、操作栈、动态链接、方法出口等信息。局部变量表存放了基本数据类型、对象引用(reference)和rerurnAddress类型(指向了一条字节码指令的地址) |
本地方法栈 | 私有 | StackOverflowError,OutOfMemoryError | 为虚拟机使用到的Native方法服务。 |
堆 | 共享 | OutOfMemoryError | 几乎所有的对象实例都在这里分配,堆是垃圾收集管理的主要区域,也被称作“GC堆”。 |
方法区 | 共享 | OutOfMemoryError | 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器后的代码等数据。运行时常量池(Runtime Constant Pool)是方法区的一部分。 |
直接内存
直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常出现。
在JDK1.4中加入了NIO(New Input/Outpur)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的 DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。
直接内存受到本机总内存的大小及处理器寻址空间的限制。
异常:OutOfMemoryError:配置虚拟机参数时,根据实际内存设置 -Xmx等参数信息,但经常忽略掉直接内存,使得各个内存区域的总和大于物理内存限制,从而导致动态扩展时出现OutOfMemoryError。