运行时数据区域
java虚拟机在运行时,它所管理的内存区域中有若干个不同的数据区域,这些数据区域有不同的名字:方法区,虚拟机栈,本地方法栈,堆,程序计数器。这些区域有的是在程序启动时就有的,有的则是随着各个用户线程而建立和销毁。下面我们来一一讲解。
1.程序计数器
程序计数器是一块较小的内存空间,它可以看作是一种指向器,指向的是java编译完成后生成的字节码文件中执行的字节码的行号。大体来看,执行java字节码时就是通过改变这个程序计数器的值来指向下一条要执行的字节码指令,如循环,跳跃,分支,异常处理,线程恢复等基础功能,但是各种不同的虚拟机可能会通过一些更高效的方式去实现执行字节码功能。
由于jvm的多线程是通过线程轮流切换并分配cpu执行时间的方式实现,那么,在任一时间,jvm只会执行唯一一个线程的中的指令。所以,为了当前线程的指令在切换后再切换回来时能够继续从刚才的位置运行,就要使用程序计数器记录停止当前线程指令运行的位置,那么,每条线程都要有一个独立的程序计数器来记录停止位置,因此,我们称这种类似程序计数器的内存区域也就是每个线程都有一个且相互独立的内存区域为“线程私有”的内存区域。
2.虚拟机栈
和程序计数器一样,虚拟机栈也是线程私有的,它的生命周期和线程相同。
对java方法执行过程有过了解的人知道,方法的执行伴随着入栈和出栈,而这里的栈指的就是虚拟机栈和本地方法栈。而这里入栈和出栈的就是“栈帧”,每一个栈帧中存储着方法的局部变量表,操作数栈,动态链接,方法出口等信息。而很多人常常把java的内存分为堆和栈,虽然这种划分有些笼统,但是这里的“栈”指的就是虚拟机栈。
如果线程所请求的栈深度大于虚拟机栈所允许的深度,将抛出StackOverflowError异常,如果虚拟机栈可以动态扩展深度,但是在扩展的时候无法申请到足够的内存,将抛出OutOfMemoryError异常。
3.本地方法栈
本地方法栈与虚拟机栈非常类似,只不过虚拟机栈执行java方法,本地方法栈执行java执行过程中用到的Native方法而已,Native方法指的是java调用的非java方法,例如C方法接口。有的虚拟机会直接把本地方法栈和虚拟机栈合二为一,和虚拟机栈一样,本地方法栈也会抛出StackOverflowError异常和OutOfMemoryError异常。
4.Java堆
java堆被所有线程共享,堆的唯一作用就是存放对象实例,几乎所有对象都在这里分配内存,但是随着技术发展,所有的对象都分配在堆上也渐渐不那么绝对了。
java堆是jvm垃圾收集器管理的主要部分,堆可能划分出多个线程私有的分配缓冲区,但是无论如何划分,每个区域存放的都是对象实例,划分的目的只是为了更好地分配和回收内存。
java堆可以处于物理上不连续的内存空间中,只要逻辑上连续即可,可以是固定大小的,也可以是可扩展的。
5.方法区
方法区是各个线程共享的内存区域,用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。