一.JVM运行时数据区包括哪几部分?

  根据《深入理解Java虚拟机》可知,运行时数据区通常包括这几个部分:程序计数器(Program Counter Register)、Java栈(VM Stack)、本地方法栈(Native Method Stack)、方法区(Method Area)、堆(Heap)。
  程序计数器、Java栈和本地方法栈统称为栈区,这部分是每个线程私有的。堆和方法区是所有线程共享的。
  
这里写图片描述

二.每部分到底存储了哪些内容?

1.程序计数器
  程序计数器(Program Counter Register),即PC寄存器。它保存的是程序当前执行的指令的地址(也可以说保存下一条指令的所在存储单元的地址),可看作当前线程所执行的字节码的行号指示器。字节解释器通过改变该值来选取下一条需要执行的指令。

  由于在JVM中,多线程是通过线程轮流切换来获得CPU执行时间的,因此,在任一具体时刻,一个CPU的内核只会执行一条线程中的指令,因此,为了能够使得每个线程都在线程切换后能够恢复在切换之前的程序执行位置,每个线程都需要有自己独立的程序计数器,并且不能互相被干扰,否则就会影响到程序的正常执行次序。因此,程序计数器是每个线程所私有的。

2.Java栈
  即虚拟机栈(Java Vitual Machine Stack),也就是我们常常所说的栈,跟C语言的数据段中的栈类似。ava栈是Java方法执行的内存模型。

  当线程执行一个方法时,就会创建一个对应的栈帧,并将建立的栈帧压栈。当方法执行完毕之后,便会将栈帧出栈。因此,线程当前执行的方法所对应的栈帧一定位于Java栈的顶部。由于每个线程当前正在执行的方法可能不同,因此每个线程都会有一个自己的Java栈,即Java栈也是线程私有的。
  每个栈帧用于存储当前方法中的变量表、参数栈和方法出口等,局部变量都存储在这里。
  局部变量表存放了编译期可知的基本数据类型、引用对象类型和返回值类型,因此局部变量表所需的内存空间在编译期就完成分配,运行期间不会改变其大小。

3.本地方法栈
  本地方法栈与Java栈的作用和原理非常相似。区别只不过是Java栈是为执行Java方法服务的,而本地方法栈则是为执行native方法(非Java语言实现)服务的。在HotSopt虚拟机中直接就把本地方法栈和Java栈合二为一。

4.堆
  Java中的堆是用来存储对象本身的以及数组(当然,数组引用是存放在Java栈中的),是垃圾收集器管理的主要区域,经常被称为GC堆。堆中包含新生代和老年代,基本都采用分代收集算法实现垃圾回收。
  堆是被所有线程共享的,在JVM中只有一个堆。

5.方法区
  在方法区中,存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量等。方法区在JVM中也是一个非常重要的区域,它与堆一样,是被线程共享的区域。

  在Class文件中除了类的字段、方法、接口等描述信息外,还有一项信息是常量池,用来存储编译期间生成的字面量和符号引用。
  在方法区中有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。当然并非Class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中,比如String的intern方法。
  HotSpot在永久代中实现方法区,但是其他JVM可能不是这样,因此方法区不一定是在永久代。

三.HotSpot对象表示机制—–OOP-Klass二分模型

  在JVM中,所有对象都继承自oopDesc类:
  

class oopDesc{
    private:
        volatile markOop _mark; //对象的运行时信息,如hashcode、GC年龄、锁等
        union _metaData{        //对象的元数据指针,指向描述对象的klass对象
            wideKlassOop _klass;
            narrowOop _compressed_klass;
        }_metaData;
    ......
}

  oopDesc父类中的_mark、 _metadata变量整体是对象的头部。
  每当调用new创建一个新对象时,都会在堆中分配内存,并创建一个对象实例,其中包含对象头和实例数据,然后将创建的结果的引用返回给Java栈中的局部变量了。通过对象头可以访问对象的运行时信息(_mark )和对象类型相关信息( _metaData指针 )了。

  HotSpot中对象访问采用直接指针的方式实现,如下图所示:
  
这里写图片描述

  Java程序是通过Java栈中的reference引用来操作堆上的具体对象的:
  1)当要访问实例数据即类中的一般属性时,可通过reference直接访问堆中对象的实例数据
  2)当要访问对象的方法和静态属性时,则通过_metaData指针访问方法区的klass对象即可
  3)由于klass对象中含有虚函数表vtbl,因此每个对象通过_metaData指针访问klass对象便可以找到所有的虚函数
  4)klass含有_super、_subclass等信息,因此可以找到每个类的所有父类、子类、兄弟类等

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐