Java虚拟机运行时数据区

屏幕快照 2018-01-03 下午10.58.25.png

所有线程共享接口:方法区,堆(Heap);

线程隔离的数据区:虚拟机栈(VM Stack),本地方法栈(Native Stack),程序计数器(Program Counter Register)


1. 程序计数器

     唯一一个没有规定任何OOM情况的区域

     当前线程所执行的字节码的行号指示器。

          — 字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的指令(分支/循环/跳转/异常处理/线程恢复等基础功能)都需要依赖这个计数器来完成

          — 多线程时,为了线程切换后能恢复正确执行位置,每条线程都需要一个独立的程序计数器,各线程之间计数器互不影响

               — 当线程正执行的是一个Java方法,计数器记录的是正在执行的虚拟机字节码指令地址

               — 正在执行的是Native方法:计数器值为空(undefined)


2. Java虚拟机栈

     线程私有的,生命周期与线程相同。

     虚拟机栈描述的是Java方法执行的内存模型。

          — 每个方法在执行的同时都会创建一个栈帧(Stack Frame)(指向堆的地址)用于存储局部变量表/操作数栈/动态连接/方法出口等信息

          — 每个方法从调用直至执行完成的过程==>一个栈帧在虚拟机栈中入栈到出栈的过程

     异常:

          — 线程请求的栈深度大于虚拟机所允许的深度:StackOverflowError异常

          — 虚拟机栈动态扩展时无法申请到足够内存:OutOfMemory

    与对象内存分配关系最密切的内存区域:堆内存和栈内存

         — 这里的栈是指虚拟机栈中局部变量/虚拟机栈表部分

         — 局部变量表存放:

              — 编译期可知的8种基本数据类型

              — 对象引用(引用类型)

              — return address类型(指向一条字节码指令的地址)


3. 本地方法栈

     虚拟机栈为虚拟机执行java方法(即字节码)服务而本地方法栈为虚拟机使用到的Native方法服务

     异常:同虚拟机栈


4. Java堆

     所有线程共享一块内存区域

     在虚拟机启动时创建,用于存放对象实例。

     根据垃圾收集器的分代收集算法,堆还可以分为:新生代,老年代…,划分的目的时为了更好的回收内存/更快地分配内存。

     Java堆可以处理逻辑上连续,物理上不连续的内存空间

          — 当前主流虚拟机都是按可扩展实现的(通过-Xmx和-Xms控制,参数一致则不可扩展)

     异常:

          — 堆中没有内存完成实例分配,且堆也无法再扩展时OOM异常


5. 方法区

     各线程共享内存区域

     用于存储已被虚拟机加载的类信息/常量/静态变量/即时编译器编译后的代码等数据

     方法区不需要连续的内存,可选择固定大小或可扩展,可选择不实现垃圾收集(但并不代表进入方法区就永久存在了)

          — 方法区的内存回收目标:针对常量池的回收和堆类型的卸载

     异常:

          — 当方法区无法满足内存分配需求时OOM异常


6. 运行时常量池

     运行时常量池是方法区的一部分。

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


7. 直接内存

     不是虚拟机运行时数据区的一部分,也与Java虚拟机规范中定义的内存区域无关

     基于通道(Channel)与缓冲区(Buffer)的I/O方式

     它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据