一、JVM体系结构
想要了解运行时数据区,先关注一下JVM的体系结构,知道数据区在JVM的整体位置和作用。
二、JVM运行时数据区
1.程序计数器
一块较小的内存空间,它是当前线程所执行的字节码的行号指示器,字节码解释器工作时通过改变该计数器的值来选择下一条需要执行的字节码指令,分支、跳转、循环等基础功能都要依赖它来实现。每条线程都有一个独立的的程序计数器,各线程间的计数器互不影响,因此该区域是线程私有的。
当线程在执行一个Java方法时,该计数器记录的是正在执行的虚拟机字节码指令的地址,当线程在执行的是Native方法(调用本地操作系统方法)时,该计数器的值为空。另外,该内存区域是唯一一个在Java虚拟机规范中没有规定任何OOM(内存溢出:OutOfMemoryError)情况的区域。
2.Java虚拟机栈—JVM Stacks
JVM栈是线程私有的,每个线程创建的同时都会创建JVM栈,java虚拟机对Java栈的两种操作:以帧为单位的压栈和弹栈。
每当线程调用一个Java方法是,虚拟机都会在该线程的Java栈中亚茹一个新的栈帧。而这个栈帧存储着该方法的参数,局部变量,中间运算结果等等数据。
栈帧
由三部分组成:局部变量、操作数栈和帧数据区
局部变量:包含对应方法的参数和局部变量,局部变量区是以字长为单位存储数据的,并且是一个从0开始计数的数组空间,数据类型int 、float、对象的引用reference和returnAddress被存储在一个存储空间,而类型byte、short和char的值存入数组前都会被转换成int值。一次也是占据一个存储空间,long和double在数组中占据连续的两个存储空间。
可以看到在0的位置会存储一个隐含的变量this,用老表示调用该方法的对象本身。
而类方法并没有this,因为类方法只与类相关。而与具体的对象无关。
操作数栈
虚拟机在操作数栈中存储的是方法运算的操作数和运算结果,操作方式是压栈和出栈,数据的存储方式和上面说的局部变量存储方式是一样的。
Java虚拟机中国是没有寄存器的,这也是符合JVM平台无关特性的一点,虚拟机把操作数栈作为他的工作区,大多数指令都会从这里弹出数据,执行运算,然后再把结果压回操作数栈
帧数据区
Java栈帧还需要一些数据来支持常量池解析、正常方法返回以及异常派发机制,这些信息都被保存在Java栈帧的帧数据区中
3.本地方法栈(Native Method Stacks)
该区域与虚拟机栈所发挥的作用非常相似,只是虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则为使用到的本地操作系统(Native)方法服务。
4.Java堆(Java Heap)
它是JVM用来存储对象实例以及数组值的区域,可以认为Java中所有通过new创建的对象的内存都在此分配,Heap中的对象的内存需要等待GC进行回收。
5.方法区域(Method Area)
方法区在JVM中也是一个非常重要的区域,它与堆一样,是被线程共享的区域。在方法区中,存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。
在Class文件中除了类的字段、方法、接口等描述信息外,还有一项信息是常量池,用来存储编译期间生成的字面量和符号引用。
在方法区中有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。当然并非Class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中,比如String的intern方法。
在JVM规范中,没有强制要求方法区必须实现垃圾回收。很多人习惯将方法区称为“永久代”,是因为HotSpot虚拟机以永久代来实现方法区,从而JVM的垃圾收集器可以像管理堆区一样管理这部分区域,从而不需要专门为这部分设计垃圾回收机制。不过自从JDK7之后,Hotspot虚拟机便将运行时常量池从永久代移除了。
总结: