内容摘抄自《深入理解Java虚拟机 第三版》
java虚拟机在运行java程序时会把内存划分为几个不同的数据区域,这些区域各有用途,创建及销毁时间。个人剧《Java虚拟机规范》规定,java虚拟机包含以下几个内存区域:程序计数器,虚拟机栈,堆,方法区,本地方法栈
程序计数器(Program Counter Register)是一块较小的内存空间,可以看做当前线程的执行字节码的行号指示器。java概念模型中,字节码解释器,就是通过改变这个计数器的值来获取下一条执行字节码指令。它是程序控制流的指示器,分支,循环,跳转,异常处理都依赖这个计数器完成。
多线程是通过线程轮流切换,分配处理器执行时间来实现的。在任意时刻,一个处理器都只会执行一条线程中的指令。为了线程切换后能够恢复正确的执行位置,每条线程都需要有一个独立的计数器,各条线程之间的计数器互不影响,独立存储。因此,这类内存区域为线程私有的内存。
如果线程执行的是java方法,那么计数器记录的是正在执行的虚拟机字节码的指令地址;如果是本地方法,这个计数器应为空。
程序计数器是规范中,唯一一个没有任何OutOfMemoryError的区域。
与程序计数器一样,java虚拟机栈(Java Virtual Machine Stack)也是线程私有的,它的声明周期与线程相同。
虚拟机栈描述的是java方法执行的线程的内训模型:方法被执行时,虚拟机会创建栈帧(stack frame)用于存储局部变量表,操作数栈,动态链接,方法出口等信息。
每一个方法被调用至执行完毕,否对应着一个栈帧在虚拟机从入栈到出栈过程。
通常情况下所说的栈就是指虚拟机栈,但大多数情况下指的是局部变量表
局部变量表存储了由编译期间可知的各种java虚拟机的基本数据类型(八中基本数据类型),对象引用(reference类型,不等同与对象本身,可能是指向对象其起始地址的指针引用,也可能是代表对象的句柄或其他)和returnAddress(指向了一条字节码指令地址)类型
这些变量在局部变量表的存储空间是以局部变量槽(slot)来表示的,64位的long和double占用2个变量槽,其余数据类型占用一个变量槽。局部变量表所需的空间在编译期间完成分配,当进入一个方法时,这个局部变量表在栈帧中分配多大的局部变量空间是确定的,运行期间不会改变表的大小。此处的大小,不是指实际内存大小,而是指局部变量槽的数量。
在《java虚拟机规范》中,对这个区域规定了两类异常状况:
1. 如果线程请求的栈的深度大于虚拟机所允许的最大深度,抛出StackOverflowError 2. 如果虚拟机的占容量可以动态扩容,当栈扩展时无法申请到足够的内存,将会抛出OutOfMemoryError异常
本地方法栈(native Method Stack)与虚拟机栈的作用十分相似。其区别在于,虚拟机栈服务于虚拟机执行的java方法(也就是字节码),二本地方法栈则是为虚拟机使用到的本地方法(Native Method)服务
与虚拟机栈一样,本地方法栈也会抛出StackOverflowError,OutOfMemoryError异常。
java方法:是由java语言编写,编译成字节码,存储在class文件中的。java方法是与平台无关的。
本地方法:本地方法是由其他语言(如C、C++ )编写,编译成和处理器相关的代码。
本地方法保存在动态连接库中,格式是各个平台专用的,运行中的java程序调用本地方法时,虚拟机装载包含这个本地方法的动态库,并调用这个方法。