jvm在执行Java程序时,会把它所管理的内存划分为若干个不同的数据区。这些区域都有各自的用途,以及创建和销毁的时间。
有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁。
一、程序计数器(线程私有)
看做当前线程所执行的行号指示器。字节码解释器工作时就是通过改变这个计数器的值类选取下一条需要执行的字节码指令。
线程私有
如果线程正在执行Java方法,计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Native方法,这个计数器则为空。
此内存区域是唯一一个JVM规范中没有规定任何OutOfMemoryError情况的区域。
二、Java虚拟机栈(线程私有)
虚拟机栈描述的是Java方法执行的内存模型。
每个方法执行时会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息。
JVM规范中,这个区域规定了两种异常状况:线程请求的栈深度大于吸泥机所允许的深度,将抛出*Error异常;如果虚拟机栈可以动态扩展,扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。
三、本地方法栈(线程私有)
与虚拟机栈所发挥的作用是非产相似的。、
区别在于虚拟机栈执行Java方法服务;而本地方法栈则为虚拟机使用到的Native方法服务。
与虚拟机栈一样,本地方法栈也会抛出上述相同的异常。
四、Java堆(线程共享)
对大多数应用说,Java堆是Java虚拟机所管理的内存中最大的一块。在虚拟机启动时创建。
目的是存放对象实例。jvm规范中描述:所有的对象实例以及数组都在堆上分配。
Java堆也是垃圾收集器管理的主要区域。(现在收集器基本都采用分代回收算法,所以可分为新生代,老年代)
这里主要说明内存区域的作用,Java堆上的上述各个区域的分配、回收等随后说明。
从内存共享的角度看,线程共享的Java堆可以划分多个线程私有的分配缓冲区(TLAB)。不过无论如何划分,都和存放内容无关,无论哪个区域,存储的都仍是对象实例。进一步划分的目的是:更好的回收内存、更快的分配内存。
最后:
1、Java堆可以处于物理上不连续的内存空间中,只要是逻辑上连续即可。
2、在实现时,既可以实现固定大小的,也可以是可扩展的。
3、当前主流的JVM都是可扩展的堆实现。如果堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。
五、方法区(线程共享)
用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
习惯在HotSpot虚拟机上开发、部署程序的开发者来说,很多人将方法区称为永久代,本质上不等价,仅仅是Hotspot虚拟机的设计团队选择吧GC分代收集扩展到方法区。(或者说用永久代来实现方法区,这样Hotspot的垃圾收集器可以像管理Java堆一样管理者部分内存,省去了专门为方法区编写内存管理工作的代码)