JVM内存区域模型:

 Java 内存分配_句柄

线程私有的

程序计数器:程序计数器是一块较小的区域,可以看做是当前线程所执行的字节码的行号指示器,是Java虚拟机中唯一没有定义OutOfMemory异常的区域。

虚拟机栈:虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会创建一个栈帧用于存储局部变量表,操作栈,动态链接,方法出口等信息。每一个方法被调用的过程就对应一个栈帧在虚拟机栈中从入栈到出栈的过程。

              Java虚拟机栈可能出现两种类型的异常:

              1. 线程请求的栈深度大于虚拟机允许的栈深度,将抛出StackOverflowError。

              2.虚拟机栈空间可以动态扩展,当动态扩展是无法申请到足够的空间时,抛出OutOfMemory异常。

本地方法栈:本地方法栈和虚拟机栈基本类似,只不过Java虚拟机栈执行的是Java代码(字节码),本地方法栈中执行的是本地方法的服务。本地方法栈中也会抛出StackOverflowError和OutOfMemory异常。

线程共享部分:

堆:堆是Java虚拟机所管理的内存中最大的一块。堆是所有线程共享的一块区域,在虚拟机启动时创建。堆的唯一目的是存放对象实例。

      Java堆可以是物理上不连续的空间,只要逻辑上连续即可,主流的虚拟机都是按照可扩展的方式来实现的。如果当前对中没有内存完成对象实例的创建,并且不能在进行内存扩展,则会抛出OutOfMemory异常。

方法区:方法区也是线程共享的区域,用于存储已经被虚拟机加载的类信息,常量,静态变量和即时编译器(JIT)编译后的代码等数据。

 

 对象访问

       对象访问在Java语言中无处不在,即使是最简单的访问,也会涉及到Java栈,java堆,方法区这三个最重要的内存区域之间的关联关系。

      主流的访问方式有两种:句柄访问方式和直接指针。

     1. 如果使用句柄访问方式,Java堆中将会划分出一块内存来作为句柄池,reference中存储的就是对象的地址,而句柄中包含了对象实例数据和类型数据各自的具体地址信息。

     Java 内存分配_java虚拟机_02

   2. 如果通过直接指针方式访问,Java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,reference中直接存储的就是对象的地址。

   Java 内存分配_java虚拟机_03

两种方式各有优势,句柄访问方式最大的好处是reference中存放的是稳定的句柄地址,在对象被移动时,只会改变句柄中的实例数据指针,而reference本身不需要被修改。

而指针访问的最大优势是速度快,它节省了一次指针定位的开销,由于对象访问在Java中非常频繁,一次这类开销积少成多后也是一项非常可观的成本。