Stack Frame JAVA运行时数据区域之栈帧

10 篇文章 0 订阅

Stack Frame

上次说了Java Run-Time Data Areas JAVA运行时数据区
这次再说一下对于Stack 中的Frames
针对Frames 也首先依旧从 Java Virtual Machine Specification中摘录一下规范的原文 Java Virtual Machine Specification 14版的2.6章节 Frames

2.6 Frames

A frame is used to store data and partial results, as well as to perform dynamic linking, return values for methods, and dispatch exceptions.
A new frame is created each time a method is invoked. A frame is destroyed when its method invocation completes, whether that completion is normal or abrupt (it throws an uncaught exception). Frames are allocated from the Java Virtual Machine stack (§2.5.2) of the thread creating the frame. Each frame has its own array of local variables (§2.6.1), its own operand stack (§2.6.2), and a reference to the run-time constant pool (§2.5.5) of the class of the current method.
A frame may be extended with additional implementation-specific information, such as debugging information.
The sizes of the local variable array and the operand stack are determined at compile-time and are supplied along with the code for the method associated with the frame (§4.7.3). Thus the size of the frame data structure depends only on the implementation of the Java Virtual Machine, and the memory for these structures can be allocated simultaneously on method invocation.
Only one frame, the frame for the executing method, is active at any point in a given thread of control. This frame is referred to as the current frame, and its method is known as the current method. The class in which the current method is defined is the current class. Operations on local variables and the operand stack are typically with reference to the current frame.
A frame ceases to be current if its method invokes another method or if its method completes. When a method is invoked, a new frame is created and becomes current when control transfers to the new method. On method return, the current frame passes back the result of its method invocation, if any, to the previous frame. The current frame is then discarded as the previous frame becomes the current one.
Note that a frame created by a thread is local to that thread and cannot be referenced by any other thread.

2.6.1. Local Variables

Each frame (§2.6) contains an array of variables known as its local variables. The length of the local variable array of a frame is determined at compile-time and supplied in the binary representation of a class or interface along with the code for the method associated with the frame (§4.7.3).
A single local variable can hold a value of type boolean, byte, char, short, int, float, reference, or returnAddress. A pair of local variables can hold a value of type long or double.
Local variables are addressed by indexing. The index of the first local variable is zero. An integer is considered to be an index into the local variable array if and only if that integer is between zero and one less than the size of the local variable array.
A value of type long or type double occupies two consecutive local variables. Such a value may only be addressed using the lesser index. For example, a value of type double stored in the local variable array at index n actually occupies the local variables with indices n and n+1; however, the local variable at index n+1 cannot be loaded from. It can be stored into. However, doing so invalidates the contents of local variable n.
The Java Virtual Machine does not require n to be even. In intuitive terms, values of types long and double need not be 64-bit aligned in the local variables array. Implementors are free to decide the appropriate way to represent such values using the two local variables reserved for the value.
The Java Virtual Machine uses local variables to pass parameters on method invocation. On class method invocation, any parameters are passed in consecutive local variables starting from local variable 0. On instance method invocation, local variable 0 is always used to pass a reference to the object on which the instance method is being invoked (this in the Java programming language). Any parameters are subsequently passed in consecutive local variables starting from local variable 1.

2.6.2. Operand Stacks

Each frame (§2.6) contains a last-in-first-out (LIFO) stack known as its operand stack. The maximum depth of the operand stack of a frame is determined at compile-time and is supplied along with the code for the method associated with the frame (§4.7.3).
Where it is clear by context, we will sometimes refer to the operand stack of the current frame as simply the operand stack.
The operand stack is empty when the frame that contains it is created. The Java Virtual Machine supplies instructions to load constants or values from local variables or fields onto the operand stack. Other Java Virtual Machine instructions take operands from the operand stack, operate on them, and push the result back onto the operand stack. The operand stack is also used to prepare parameters to be passed to methods and to receive method results.
For example, the iadd instruction (§iadd) adds two int values together. It requires that the int values to be added be the top two values of the operand stack, pushed there by previous instructions. Both of the int values are popped from the operand stack. They are added, and their sum is pushed back onto the operand stack. Subcomputations may be nested on the operand stack, resulting in values that can be used by the encompassing computation.
Each entry on the operand stack can hold a value of any Java Virtual Machine type, including a value of type long or type double.
Values from the operand stack must be operated upon in ways appropriate to their types. It is not possible, for example, to push two int values and subsequently treat them as a long or to push two float values and subsequently add them with an iadd instruction. A small number of Java Virtual Machine instructions (the dup instructions (§dup) and swap (§swap)) operate on run-time data areas as raw values without regard to their specific types; these instructions are defined in such a way that they cannot be used to modify or break up individual values. These restrictions on operand stack manipulation are enforced through class file verification (§4.10).
At any point in time, an operand stack has an associated depth, where a value of type long or double contributes two units to the depth and a value of any other type contributes one unit.

2.6.3. Dynamic Linking

Each frame (§2.6) contains a reference to the run-time constant pool (§2.5.5) for the type of the current method to support dynamic linking of the method code. The class file code for a method refers to methods to be invoked and variables to be accessed via symbolic references. Dynamic linking translates these symbolic method references into concrete method references, loading classes as necessary to resolve as-yet-undefined symbols, and translates variable accesses into appropriate offsets in storage structures associated with the run-time location of these variables.
This late binding of the methods and variables makes changes in other classes that a method uses less likely to break this code.

2.6.4. Normal Method Invocation Completion

A method invocation completes normally if that invocation does not cause an exception (§2.10) to be thrown, either directly from the Java Virtual Machine or as a result of executing an explicit throw statement. If the invocation of the current method completes normally, then a value may be returned to the invoking method. This occurs when the invoked method executes one of the return instructions (§2.11.8), the choice of which must be appropriate for the type of the value being returned (if any).
The current frame (§2.6) is used in this case to restore the state of the invoker, including its local variables and operand stack, with the program counter of the invoker appropriately incremented to skip past the method invocation instruction. Execution then continues normally in the invoking method’s frame with the returned value (if any) pushed onto the operand stack of that frame.

2.6.5. Abrupt Method Invocation Completion

A method invocation completes abruptly if execution of a Java Virtual Machine instruction within the method causes the Java Virtual Machine to throw an exception (§2.10), and that exception is not handled within the method. Execution of an athrow instruction (§athrow) also causes an exception to be explicitly thrown and, if the exception is not caught by the current method, results in abrupt method invocation completion. A method invocation that completes abruptly never returns a value to its invoker.

总结一下

大概解释一下

栈帧是用于存储数据和部分结果,以及执行动态链接、方法返回值和派发异常或者说抛出的异常。
每次调用方法时都会创建一个新栈帧。当一个栈帧的方法调用完成时,不管该完成是正常的还是异常的,它都会被destroyed。
栈帧内存空间是从当前线程的Java虚拟机堆栈分配出来的。
每个帧都有自己的局部变量数组 、自己的操作数堆栈 和对当前方法类的运行时常量池 的引用。
栈帧可以 用 additional implementation-specific information 扩展 (例如调试信息)。
局部变量数和操作指令集合是再编译为class时候就已经确定了的。
栈帧占用的的大小取决于java虚拟机的实现。
一个线程只有一个栈帧是处于活动状态的,这个栈帧叫当前栈帧,方法名称是当前运行的方法名称,类也是当前运行类。
当再一个方法中调用另外一个方法时当前栈帧就会变为另外一个栈帧。当有返回的值的时候当前栈帧会将指令结果传回上一个栈帧中。当前栈帧变为上一个栈帧,然后销毁这个栈帧。
注意,一个线程创建的栈帧就是当前线程的,这个栈帧是不可以被其他线程引用的。

再来看看栈帧中的结构

  1. Local Variables 栈帧的本地变量。
    其实这里就是方法中所包含的所有变量,包含基础类型和对象类型的引用。知道class结构的都知道。java再被编译为class后这些变量都存在了本地变量池中了。这里也只是引用而已。
  2. Operand Stacks 操作堆栈,
    这里其实就是class中的code,指令集合这里就包含很多的jvm中的指令,具体指令需要看具体方法中干了什么
  3. Dynamic Linking 动态链接
    这个是在java支持了动态语句(lambda这种符号调用)或者是调用反射的时候所搞出来的玩意,指的就是在运行时进行的动态指向或者动态创建引用。一般做这要经过 先查找,然后创建一个新的Class对象扔到method area区去,然后创建新的新的对象引用。
  4. Normal Method Invocation Completion 正常的返回 正常的返回比如返回值啊或者通过代码throws出来的都算是正常的返回,这个是有返回值的。
  5. Abrupt Method Invocation Completion 异常的返回 这个是没有返回值的,代码运行异常时候的返回。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JVM运行时数据区是指在JVM进程运行过程中,用于存储数据的各个区域,包括以下几个部分: 1. 程序计数器(Program Counter Register):线程私有的区域,用于记录当前线程执行的字节码行号,以便线程在执行过程中能够从正确的位置继续执行。 2. Java虚拟机栈(JVM Stack):线程私有的区域,用于存储方法的栈帧Stack Frame),包括局部变量表(Local Variable Table)、操作数栈(Operand Stack)、动态链接(Dynamic Linking)、方法出口(Return Address)等信息。当一个方法被调用时,JVM会在栈中为该方法分配一个栈帧,方法执行完毕后,栈帧被出栈,JVM继续执行上一个栈帧中的方法。 3. 本地方法栈(Native Method Stack):与Java虚拟机栈类似,用于存储本地方法的栈帧,本地方法是指使用本地语言(如C语言)编写的方法,本地方法栈与Java虚拟机栈的区别在于,本地方法栈用于执行本地方法,而Java虚拟机栈用于执行Java方法。 4. 堆(Heap):所有线程共享的区域,用于存储对象实例。在JVM启动时,会分配一定大小的堆空间,当堆空间不足时,JVM会自动进行垃圾回收以释放无用的对象,从而维持堆空间的稳定。 5. 方法区(Method Area):所有线程共享的区域,用于存储类的元数据信息,包括类名称、访问修饰符、常量池、字段描述符、方法描述符等信息。在JDK8及以前的版本中,方法区也包括永久代(Permanent Generation),用于存储一些与类加载、字符串常量等相关的信息。在JDK8及以后的版本中,永久代被移除,取而代之的是Metaspace(元空间),用于存储类的元数据信息。 6. 运行时常量池(Runtime Constant Pool):方法区的一部分,用于存储编译期生成的各种字面量(Literal)、符号引用(Symbolic Reference)等信息。在方法调用时,JVM会将运行时常量池中的字面量、符号引用等信息加载到操作数栈中,供指令使用。 除了以上几个区域外,还有一些其他的区域,例如直接内存(Direct Memory),用于存储通过NIO(New IO)库分配的堆外内存。JVM运行时数据区域对于Java程序的执行起到了关键的作用,了解这些区域有助于我们更好地理解Java程序的执行过程和内存管理机制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值