JVM虚拟机(一)

运行时的数据区域

Java虚拟机在执行Java程序是会将所管理的内存分为若干个区域

转载
方法区与堆区为线程共享数据区,虚拟机栈,本地方法栈,程序计数器为线程私有。

程序计数器

程序计数器是一块较小的内存空间。可以看做是当前线程所执行的字节码的行号指示器。字节码解释器通过改变计数器的数值来选取下一条需要执行的字节码指令。多线程时,每个线程都有一个独立的程序计数器,每个计数器之间互不影响。
如果线程正在执行一个Java方法,计数器记录的是正在执行的虚拟机字节码指令的地址。当执行本地方法时,计数器为空(Undefined)
它是程序控制流的指示器,分支,循环,跳转,异常处理,线程恢复等基础功能都需要依赖这个计数器来完成

Java虚拟机栈

生命周期和线程相同,虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行时,Java虚拟机都会同步创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法被调用直至执行完毕的过程就对应着一个栈帧从虚拟机栈入栈到出栈的过程。
局部变量表存放了编译期可知的各种Java虚拟机基本数据类型(boolean,byte,char,short,int,float,long,double),对象引用(不等于对象本身,可能是一个指向对象起始地址的引用指针,也可能是一个代表对象的句柄或其他于此对象相关的位置),returnAddress(指向了一条字节码指令的地址)。
这些数据类型在局部变量表中的存储空间以局部变量槽(slot)表示,其中64位的long和double类型的数据会占用两个变量槽,其余只占一个。
局部变量表所需内存空间在编译期完成分配,进入方法时分配的内存是确定的,运行期间不会改变局部变量表的大小(大小指槽的数量)
如果线程请求的深度大于虚拟机所允许的深度,将抛出StackOverflowError异常,如果Java虚拟机栈容量可动态扩展,当栈扩展时,无法申请到足够的内存会抛出OutMemoryError异常。

本地方法栈

虚拟机栈为执行Java方法(字节码)服务,本地方法栈为虚拟机执行本地方法服务。

Java堆

虚拟机所管理内存最大的一块,被所有线程所共享,在虚拟机启动是创建,此区域的唯一目的是存放对象实例。
从分配内存的角度看,Java堆可以划分出多个线程私有的分配缓冲区(TLAB),以提升对象分配时的效率。
堆栈无法扩展时,虚拟机会抛出OutMemoryError异常。

方法区

线程共享区,用来存储虚拟机加载的类型信息,常量,静态变量,即时编译器编译后的代码缓存等数据。
方法区无法满足新的内存分配需求时,将抛出OutOfMemoryError异常。

运行时常量池

运行时常量池是方法区的一部分,Class文件中除了有类的版本,字段,方法,接口等描述信息外,还有常量信息表,用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
常量池无法在申请到内存时会抛出OutOfMemoryError异常。

直接内存

直接内存不是Java虚拟机运行时数据区中的一部分。 JDK1.4 中新加入了NIO(New Input/Output)类,引入了基于通道与缓冲区的I/O方式,他可以使用Native函数库直接分配对外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能。