运行时数据区

运行时数据区

 Java虚拟机定义了若干种程序运行期间会使用到的运行时数据区,其中有一些会随着虚拟机启动而创建,随着虚拟机退出而销毁。另外一些则是与线程一一对应的,这些与线程对应的数据区域会随着线程开始和结束而创建和销毁。


PC 寄存器

 Java虚拟机可以支持多条线程同时执行,每一条Java虚拟机线程都有自己的PC寄存器。在任意时刻,一条Java虚拟机线程只会执行一个方法的代码,这个正在被线程执行的方法称为该线程的当前方法(Current Method)。如果这个方法不是native的,那PC寄存器就保存Java虚拟机正在执行的字节码指令的地址,如果该方法是native的,那PC寄存器的值是undefined。PC寄存器的容量至少应当能保存一个returnAddress类型的数据或者一个与平台相关的本地指针的值。

 

Java 虚拟机栈

  每一条Java虚拟机线程都有自己私有的Java虚拟机栈(Java Virtual Machine Stack),这个栈与线程同时创建,用于存储栈帧(Frames)。Java虚拟机栈的作用与传统语言(例如C语言)中的栈非常类似,就是用于存储局部变量与一些过程结果的地方。另外,它在方法调用和返回中也扮演了很重要的角色。因为除了栈帧的出栈和入栈之外,Java虚拟机栈不会再受其他因素的影响,所以栈帧可以在堆中分配,Java虚拟机栈所使用的内存不需要保证是连续的。

 Java虚拟机规范允许Java虚拟机栈被实现成固定大小的或者是根据计算动态扩展和收缩的。如果采用固定大小的Java虚拟机栈设计,那每一条线程的Java虚拟机栈容量应当在线程创建的时候独立地选定。Java虚拟机实现应当提供给程序员或者最终用户调节虚拟机栈初始容量的手段,对于可以动态扩展和收缩Java虚拟机栈来说,则应当提供调节其最大、最小容量的手段。

Java 堆

  在Java虚拟机中,堆(Heap)是可供各条线程共享的运行时内存区域,也是供所有类实例数组对象分配内存的区域。

 Java堆在虚拟机启动的时候就被创建,它存储了被自动内存管理系统(Automatic Storage Management System,也即是常说的“Garbage Collector(垃圾收集器)”)所管理的各种对象,这些受管理的对象无需,也无法显式地被销毁。本规范中所描述的Java虚拟机并未假设采用什么具体的技术去实现自动内存管理系统。虚拟机实现者可以根据系统的实际需要来选择自动内存管理技术。Java堆的容量可以是固定大小的,也可以随着程序执行的需求动态扩展,并在不需要过多空间时自动收缩。Java堆所使用的内存不需要保证是连续的。

 Java虚拟机实现应当提供给程序员或者最终用户调节Java堆初始容量的手段,对于可以动态扩展和收缩Java堆来说,则应当提供调节其最大、最小容量的手段。 

 

方法区

在Java虚拟机中,方法区(Method Area)是可供各条线程共享的运行时内存区域。方法区与传统语言中的编译代码储存区(Storage Area Of Compiled Code)或者操作系统进程的正文段(Text Segment)的作用非常类似,它存储了每一个类的结构信息,例如运行时常量池(Runtime Constant Pool)、字段和方法数据、构造函数和普通方法的字节码内容、还包括一些在类、实例、接口初始化时用到的特殊方法

方法区在虚拟机启动的时候被创建,虽然方法区是堆的逻辑组成部分,但是简单的虚拟机实现可以选择在这个区域不实现垃圾收集。这个版本的Java虚拟机规范也不限定实现方法区的内存位置和编译代码的管理策略。方法区的容量可以是固定大小的,也可以随着程序执行的需求动态扩展,并在不需要过多空间时自动收缩。方法区在实际内存空间中可以是不连续的。

 Java虚拟机实现应当提供给程序员或者最终用户调节方法区初始容量的手段,对于可以动态扩展和收缩方法区来说,则应当提供调节其最大、最小容量的手段。

运行时常量池

   运行时常量池(Runtime Constant Pool)是每一个类或接口的常量池(Constant_Pool,的运行时表示形式,它包括了若干种不同的常量:从编译期可知的数值字面量到必须运行期解析后才能获得的方法或字段引用。运行时常量池扮演了类似传统语言中符号表(Symbol Table)的角色,不过它存储数据范围比通常意义上的符号表要更为广泛。每一个运行时常量池都分配在Java虚拟机的方法区之中,在类和接口被加载到虚拟机后,对应的运行时常量池就被创建出来。

本地方法栈

Java虚拟机实现可能会使用到传统的栈(通常称之为“C Stacks”)来支持native方法(指使用Java以外的其他语言编写的方法)的执行,这个栈就是本地方法栈(NativeMethod Stack)。当Java虚拟机使用其他语言(例如C语言)来实现指令集解释器时,也会使用到本地方法栈。如果Java虚拟机不支持natvie方法,并且自己也不依赖传统栈的话,可以无需支持本地方法栈,如果支持本地方法栈,那这个栈一般会在线程创建的时候按线程分配。

 Java虚拟机规范允许本地方法栈被实现成固定大小的或者是根据计算动态扩展和收缩的。如果采用固定大小的本地方法栈,那每一条线程的本地方法栈容量应当在栈创建的时候独立地选定。一般情况下,Java虚拟机实现应当提供给程序员或者最终用户调节虚拟机栈初始容量的手段,对于长度可动态变化的本地方法栈来说,则应当提供调节其最大、最小容量的手段。

栈帧

栈帧(Frame)是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接(DynamicLinking)、方法返回值和异常分派(DispatchException)。 栈帧随着方法调用而创建,随着方法结束而销毁——无论方法是正常完成还是异常完成(抛出了在方法内未被捕获的异常)都算作方法结束。栈帧的存储空间分配在Java虚拟机栈之中,每一个栈帧都有自己的局部变量表(Local Variables)、操作数栈(Operand Stack)和指向当前方法所属的类的运行时常量池的引用。

局部变量表和操作数栈的容量是在编译期确定,并通过方法的Code属性保存及提供给栈帧使用。因此,栈帧容量的大小仅仅取决于Java虚拟机的实现和方法调用时可被分配的内存。 在一条线程之中,只有目前正在执行的那个方法的栈帧是活动的。这个栈帧就被称为是当前栈帧(Current Frame),这个栈帧对应的方法就被称为是当前方法(Current Method),定义这个方法的类就称作当前类(Current Class)。对局部变量表和操作数栈的各种操作,通常都指的是对当前栈帧的对局部变量表和操作数栈进行的操作。如果当前方法调用了其他方法,或者当前方法执行结束,那这个方法的栈帧就不再是当前栈帧了。当一个新的方法被调用,一个新的栈帧也会随之而创建,并且随着程序控制权移交到新的方法而成为新的当前栈帧。当方法返回的之际,当前栈帧会传回此方法的执行结果给前一个栈帧,在方法返回之后,当前栈帧就随之被丢弃,前一个栈帧就重新成为当前栈帧了。

栈帧是线程本地私有的数据,不可能在一个栈帧之中引用另外一条线程的栈帧。

局部变量表

每个栈帧内部都包含一组称为局部变量表(Local Variables)的变量列表。栈帧中局部变量表的长度由编译期决定,并且存储于类和接口的二进制表示之中,既通过方法的Code属性保存及提供给栈帧使用。

一个局部变量可以保存一个类型为boolean、byte、char、short、float、reference和returnAddress的数据,两个局部变量可以保存一个类型为long和double的数据。 局部变量使用索引来进行定位访问,第一个局部变量的索引值为零,局部变量的索引值是从零至小于局部变量表最大容量的所有整数。

long和double类型的数据占用两个连续的局部变量,这两种类型的数据值采用两个局部变

量之中较小的索引值来定位。例如我们讲一个double类型的值存储在索引值为n的局部变量中,实际上的意思是索引值为n和n+1的两个局部变量都用来存储这个值。索引值为n+1的局部变量是无法直接读取的,但是可能会被写入,不过如果进行了这种操作,就将会导致局部变量n的内容失效掉。

上文中提及的局部变量n的n值并不要求一定是偶数,Java虚拟机也不要求double和long类型数据采用64位对其的方式存放在连续的局部变量中。虚拟机实现者可以自由地选择适当的方式,通过两个局部变量来存储一个double或long类型的值。

Java虚拟机使用局部变量表来完成方法调用时的参数传递,当一个方法被调用的时候,它的参数将会传递至从0开始的连续的局部变量表位置上。特别地,当一个实例方法被调用的时候,第0个局部变量一定是用来存储被调用的实例方法所在的对象的引用(即Java语言中的“this”关键字)。后续的其他参数将会传递至从1开始的连续的局部变量表位置上。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值