JAVA虚拟机在执行JAVA程序的时候,会将它管理的内存区域划分为五个不同的数据区域,它们都有各自的用途以及生成和销毁时间。一些区域随着进程的启动而生成,有些区域则随着用户线程的启动和结束而生成和销毁。
一、JAVA虚拟机包含的运行时内存区域分为以下五个部分,分别是程序计数器、方法区、本地方法栈、虚拟机栈、堆。以下分别做介绍。其中方法去和堆属于多个线程共有,程序计数器、本地方法栈、虚拟机栈属于所有线程特有。这里一定注意,这五个区域是运行时数据区域,因为有的数据区域只有在JAVA程序运行或者线程启动后才会生成。区域划分如下图所示:
JAVA虚拟机运行时数据区域划分
1、程序计数器
在JAVA虚拟机中,程序计数器是比较小的一个内存区域,是线程私有的,不对其他线程共享,因此不受其他线程影响。它的作用是记录当前线程执行字节码的行号指示器,存储当前字节码行号地址。字节码解释器就是通过改变程序计数器,找到字节码下一条指令,像异常,循环,线程恢复都是依赖于这个计数器来实现的。JAVA多线程的执行方式是通过线程切换,给定处理器执行时间轮流切换完成的。当多个线程轮流切换,继续执行程序的时候,为了保证线程执行的准确性,也是依赖程序计数器来完成的,它会记录上次执行时候的字节码行号,下次继续执行的时候从该行号的下一个行号开始执行。因为它是线程私有的,所以程序计数器不受线程之间影响。同时它也是JAVA虚拟机规范中唯一一个没有固定OutOfMemeryError异常的内存区域。
2、虚拟机栈
需要注意的是,虚拟机栈和程序计数器相同,都是线程私有的,随着线程的启动和停止而产生和消亡。它的作用是描述JAVA方法执行时候的内存模型,既每个方法执行的时候,都会创建一个栈帧,栈帧保存着方法的局部变量表,方法出口等信息。在JAVA虚拟机规范中对这个区域规定了两种异常状态:当线程请求的栈深度大于JAVA虚拟机允许的最大栈深度,会出现StackOverflowError异常,当JAVA虚拟机进行扩展的时候,如果无法申请到足够的内存空间,就会抛出OutOfMemeryError异常。
3、本地方法栈
本地方法栈的作用和虚拟机方法栈的作用类似。虚拟机方法栈为虚拟机运行的JAVA程序提供服务,本地方法栈为本地使用Native的方法提供服务,在JAVA虚拟机规范中也规定了两种异常类型,分别是StackOverflowError异常和OutOfMemeryError异常。
4、堆
堆内存是JAVA虚拟机中最大的一块内存空间,所有线程共享,用于存放所有的JAVA实例对象(包括数组)。堆内存也是JAVA虚拟机垃圾回收主要管理的一个区域,从内存回收角度来看,分为老年代和新生代。从内存分配角度来看,可以分为不同线程的私有分配缓冲区。无论如何分配,保存的内容不变,全是对象的实例。堆内存可以是不连续的内容空间,只要逻辑上是连续的即可,可以通过Xmx和Xms来进行扩展,当无法扩展,或者无法分配足够的内存空间的时候,会出现内存溢出异常OutOfMemeryError。
5、方法区
方法区也是所有线程共享的,它保存的是加载的类,常量,静态变量,编译后的代码等,也是可以不连续的内存空间,也可以扩展,扩展方法和堆一样,这个区域可以选择不进行垃圾回收,当方法区无法满足内存分配需要时,会抛出内存溢出异常OutOfMemeryError。运行时常量池是方法区的一个部分,用于存放编译期的字面值常量和符号引用。