java运行时数据区(pc寄存器、虚拟机栈(栈帧)、本地方法栈、java堆、方法区、运行时常量池、直接内存)

2.1 概述

在这里插入图片描述


2.2.1 pc寄存器(程序计数器)

程序计数器是一块较小的内存空间,线程私有。它可以看作是当前线程所执行的字节码的行号指示器

字节码指令集,由操作码和操作数组成,.java文件编译后的.class就是操作数和操作码的集合。     .class文件的行号,记住指令运行到哪了;

每一条java虚拟机线程都有自己的pc寄存器,任何时刻,一个Java线程只会执行一个方法(该线程的当前方法)的代码;
    如果该方法是nativa,那么pc寄存器得值是undefined,如果不是native的,那么pc寄存器就保存jvm正在执行的字节码指令地址;
    程序计数器是唯一一块Java虚拟机规范中没有任何规定的OutOfMemoryError情况的区域;


2.2.2 Java虚拟机栈

用于存储栈帧


栈帧

用于存储局部变量表,操作数栈,动态链接,方法出口等信息;

https://blog.csdn.net/a616413086/article/details/51272309

每一个方法从被调用到执行完毕,都对应者一个栈帧在虚拟机栈中入栈到出栈的过程。


1 局部变量表

每个栈帧都包含一组变量列表,称之为局部变量表,用于存储 方法的形参方法内部定义的变量(局部变量)(这些变量也包括引用类型),和returnAddress(返回地址类型);
    局部变量表是编译期可知
    注意,引用类型的局部变量也会存储在局部变量表中,只不过存储的是引用类型的地址。

jvm使用局部变量表完成方法调用时方法的传递。通过索引来访问,第0个局部变量一定用来存储该实例方法所在对象的引用(this),static修饰的方法除外;

系统不会为局部变量赋予初始值(但是实例变量和类变量都会被赋予初始值)。也就是说局部变量表不存在类变量那样的准备阶段。


Solt

局部变量表的容量以变量槽(Slot)为最小单位,其中64位的double和long占用2个连续的solt,访问时访问索引小的solt,
    对于byte、short以及char类型的值在局部变量表之前,会被转换为int。
    因为方法的参数和局部变量,在编译器就已经确定了,所以局部变量表的空间大小在编译器就已经确定。


2 操作数栈

是用来描述Java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧。

void spin(){
    int i ;
    for (i = 0;i<100;i++){
        ....
    } 
}

这个方法中, 0,100两个常量存放在操作数栈 整形变量i存放在局部变量变量表中

是一个后入先出栈(LIFO)。同局部变量表一样,操作数栈的最大深度也在编译的时候写入到方法的Code属性的max_stacks数据项中。

虚拟机在操作数栈中存储数据的方式和在局部变量区中是一样的,对于byte、short以及char类型的值在压入到操作数栈之前,也会被转换为int。

所有的操作码都是对操作数栈上的数据进行操作,对于每一个方法的调用,JVM会建立一个操作数栈,以供计算使用。和局部变量一样。操作数栈的最大深度也是编译的时候写入到方法表的code属性的max_stacks数据项中。
    另外,在概念模型中,两个栈帧作为虚拟机栈的元素,相互之间是完全独立的,但是大多数虚拟机的实现里都会作一些优化处理,令两个栈帧出现一部分重叠。

3 动态链接

一个引用,(当前方法所在类型的)运行时常量池的引用

https://zhuanlan.zhihu.com/p/45354152

在一个class文件中,一个方法要调用其他方法,需要将这些方法的符号引用转化为其在内存地址中的直接引用,而符号引用存在于方法区中的运行时常量池。
    Java虚拟机栈中,每个栈帧都包含一个指向运行时常量池中该栈所属方法的符号引用,持有这个引用的目的是为了支持方法调用过程中的动态链接(Dynamic Linking)
    这些符号引用一部分会在类加载阶段或者第一次使用时就直接转化为直接引用,这类转化称为静态解析。另一部分将在每次运行期间转化为直接引用,这类转化称为动态连接。在一个class文件中,一个方法要调用其他方法,需要将这些方法的符号引用转化为其在内存地址中的直接引用,而符号引用存在于方法区中的运行时常量池。


2.2.3 本地方法栈

与Java虚拟机栈类似,用来支持native方法(除java语言外的语言编写的方法)的栈,与之对应的虚拟机栈是为java方法(字节码)服务


2.2.4 Java堆

用于存放对象实例。(几乎所有的对象实例以及数组都在这里存放)
在这里插入图片描述

JVM管理的内存中最大的一块。
分为新生代和老年代,新生代再细分有Edon,Form Suivivor,To Suivivor等。
线程共享的Java堆可能划分出多个线程私有的分配缓冲区(TLAB)。
Java堆 物理上不连续,逻辑连续。


2.2.5 方法区

JDK7 之前用于存储已被虚拟机加载的类信息、常量、字符串常量、类静态变量、即时编译器编译后的代码等数据。

  • 也被称为永久代(被称为永久代HotSpot设计团队希望将GC分带手机扩展到方法区)
  • 线程共享
  • 会抛出 OutOfMemoryError 异常

执行过程 eg

cn.test.MainDemo{
main方法{
A a = new A()
}
}

先 javac +类名进行编译,然后java+类名运行,此时加载cn.test.MainDemo到方法区,main方法入栈,遇到new A()时(遇到A a时,并不加载A的代码到方法区,只有创建对象时(new)才加载)A的构造器入栈


2.2.6 运行时常量池

用于存放编译期生成的各种字面量和符号引用

  • 是方法区的一部分。
  • Class文件中除了有类的版本/字段/方法/接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将类在加载后进入方法区的运行时常量池中存放。运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的是 String.intern()方法
  • 受方法区内存的限制,当常量池无法再申请到内存时会抛出 OutOfMemoryError 异常。

在这里插入图片描述

存储(方法或者类的)变量字面量(编译时能知道)和运行时解析才知道的方法或者字段引用, 在方法区分配

在目前已经发布的JDK1.7的HotSpot中,已经把原来存放在方法区中的字符串常量池移出。根据查阅的资料显示在JDK1.7以后的版本中字符串常量池移到堆内存区域


2.2.7 直接内存

直接内存不是jvm运行时数据区的一部分,也不是jvm规范中定义的内存区域,

  • 直接内存也被频繁使用,也会出现OOM异常。

的HotSpot中,已经把原来存放在方法区中的字符串常量池移出。根据查阅的资料显示在JDK1.7以后的版本中字符串常量池移到堆内存区域


2.2.7 直接内存

直接内存不是jvm运行时数据区的一部分,也不是jvm规范中定义的内存区域,

  • 直接内存也被频繁使用,也会出现OOM异常。

猜你喜欢

转载自blog.csdn.net/qq_43369986/article/details/108996832