Java虚拟机运行时数据区
程序计数器:可以简单看作是当前线程所执行的字节码的行号指示器。字节码解释器就是通过改变程序计数器来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等都是依赖计数器完成的。JVM的多线程是通过线程轮流切换来实现的,一个处理器在任何一个确定的时刻都只会执行一条线程的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都有一个独立的程序计数器。如果线程执行的是一个Java方法,那么计数器记录的是当前虚拟机字节码指令的地址;如果线程执行的是Native方法,那么计数器值为空。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
Java虚拟机栈:Java虚拟机栈也是线程私有的,它的生命周期和线程相同。虚拟机栈描述了Java方法执行的内存模型:每个方法在执行同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用到执行完成都对应一个栈帧在虚拟机栈中的入栈到出栈的过程。
局部变量表所需的内存空间在编译期间就完成分配了,运行期不能修改大小。
针对异常状况,当线程请求的栈深度大于虚拟机栈深度,将抛出StackOverflowError异常,当虚拟机栈扩展时无法申请到足够的内存,将抛出OutOfMemoryError异常。
本地方法栈:与虚拟机栈相似,虚拟机栈为执行Java方法服务,本地方法栈为Native方法服务(有的虚拟机(Sun HotSpot虚拟机)将二者(本地方法栈和虚拟机栈)合二为一)。
Java堆:是被所有线程共享的一块内存区域。它在虚拟机启动时创建,它用于存放对象实例,它是垃圾收集管理的主要区域(称为“GC堆”)。Java堆还可以细分为:新生代和老年代。Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。它可以通过-Xmx和-Xms控制扩展,如果在堆中没有内存完成实例分配,并且堆无法扩展时,将抛出OutOfMemoryError异常。
方法区:各个线程共享的内存区域。它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。
运行时常量池:常量池用于存放编译期生成的各种字面量和符号引用,这些内容在类加载后进入方法区的运行时常量池中存放。Java程序运行期间也可能将数据放入常量池中(String类的intern()方法)。当常量池无法申请到内存时,将抛出OutOfMemoryError异常。
直接内存:服务器管理员在配置虚拟机参数时,会根据实际内存设置-Xmx等参数信息,但经常忽略直接内存,使得各个内存区域总和大于物理内存限制(包括物理的和操作系统级的限制),从而导致动态扩展时出现OutOfMemoryError异常。