深入JVM 运行时数据区域

——Java虚拟机在执行Java程序时将其所管理的内存划分为若干区域,各个区域都有各自的用途,以及创建和销毁的

时间,有的区域随虚拟机进程的启动存在,有的依赖于用户线程的启动和结束而建立和销毁

 

——Java虚拟机所管理的内存包含以下几个运行时数据区域

 

深入JVM 运行时数据区域

——程序计数器

pc程序计数器是一块占用空间较小的区域,是当前线程执行的字节码的指示器。在虚拟机的概念模型中,字节码解释器工作是通过改变计数器的值来控制下一条的字节码指令,分支,循环,跳转等功能均由该计数器完成

Java虚拟机的多线程执行是通过线程切换来分配时间片,任何一个确定时刻,一个处理器只会执行一条线程的指令。因此,线程的切换需要 还原现场  ,因此每个线程都有其单独的程序计数器,即该内存为线程私有

主要分为2钟情况,如果执行的是一个Java方法,计数器记录正在执行的虚拟机字节码的地址。如果执行的是本地native方法,计数器赋值为空。

通常该区域占用空间很少,不会出现OutOfMemoryError空间不足错误

——Java虚拟机栈

与程序计数器一样,Java虚拟机栈(Java Virtual Machine Stacks)为线程私有。它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法执行的同时会创建一个栈帧用于存储局部变量表,操作数栈,方法出口等。每个方法从直接调用直至执行完成过程,都对应一个栈帧在虚拟机栈的入栈出栈过程

局部变量表:存放了编译器可知的各种基本类型(8种)、对象引用、不同于对象实例本身,可能只是一个对象初始地址的引用指针,也可能是指向一个代表对象的句柄

该区域规定了2种错误:弱国线程请求的栈深度大于虚拟机允许的最大深度,将出现StackOverFlowError错误,如果在拓展过程无法申请到足够的内存,将出现OutOfMemoryError错误

——本地方法栈

本地方法栈与虚拟栈的作用类似,区别在于虚拟机栈执行的是Java方法(字节码过程) ,而本地方法栈用到的是本地Native服务。其中本地方法栈使用的语言、数据结构可自由实现。

可能出现的错误异常:StackOfflowError   OutOfMemoryError

——Java堆

对于大多数应用而言,Java堆是Java虚拟机所管理内存种最大的部分,Java堆是线程共享的,在虚拟机启动时便创建。此内存区域的目的是存放对象实例,几乎所有的对象实例和数组都在堆中分配内存。

Java堆是垃圾收集(GC)的主要工作区域,因此也被称为GC 堆 。从内存回收的角度看,收集器主要采用分代收集算法,即Java堆可以分为:新生代 老生代 。

从内存分配的角度来看,线程共享的Java堆可能划分出多个线程私有的分配缓冲区,进一步划分的目的是更好的内存分配

物理特性:Java堆可以处在物理上不连续的内存区域,只要逻辑上是连续的即可,既可以固定大小,也可以可拓展,主流的采用可拓展实现( -Xmx -Xms)控制 当堆大小无法拓展时,抛出OutOfMemoryError错误异常。

——方法区

方法区和Java堆一样,是多个线程所共享的内存区域,用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译的代码.通常该区域也习惯被称为 永生代。

Java虚拟机规范对方法区的限制非常宽松,除了和Java堆一样不需要连续的内存空间,还可以选择不实现垃圾回收,即垃圾回收在该区域较少出现,当无法满足分配内存时,出现OutOfMemoryError错误异常

——运行时常量池

运行时常量池(Runtime Constant Pool)是方法区的一部分。Class文件种除有类的版本、字段、方法、接口等描述信息外,还有常量池,存放编译器生成的各种    字面量   符号引用 这部分内容在类加载后进入方法区的常量池存放

运行时常量池相对于Class文件常量池具备动态性,Java语言并非要求常量仅仅在编译期产生,运行期间也可以将常量放入池中,如String.intern()方法