美文网首页
JVM-从字节码到运行时(2)

JVM-从字节码到运行时(2)

作者: 甜甜起司猫_ | 来源:发表于2021-04-17 23:26 被阅读0次

JVM-从字节码到运行时(2)


基于栈的解释器执行过程

  public int add(int);
    descriptor: (I)I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=2
         0: sipush        300
         3: istore_2
         4: aload_0
         5: getfield      #4                  // Field a:I
         8: sipush        200
        11: iadd
        12: iload_2
        13: iadd
        14: iload_1
        15: imul
        16: ireturn
      LineNumberTable:
        line 10: 0
        line 11: 4

这是例子ByteCodeDemo类中的add(int z)方法。

descriptor: (I)I // 该方法入参类型为int,返参类型为int
flags: ACC_PUBLIC // 该方法的访问标志为PUBLIC
stack=2, locals=3, args_size=2
  • stack表示操作数栈栈深为2
  • locals表示局部变量表有3个变量槽位slot
  • args_size表示方法内变量数量,包括入参和方法内定义的局部变量
         0: sipush        300
         3: istore_2
         4: aload_0
         5: getfield      #4                  // Field a:I
         8: sipush        200
        11: iadd
        12: iload_2
        13: iadd
        14: iload_1
        15: imul
        16: ireturn

左边数字为指令的偏移地址,右边为助记符

关于助记符程序计数器局部变量表操作栈在方法执行时的大致过程:
(此处应有图)

为什么说执行过程是基于栈呢?

Java虚拟机以方法作为最基本的执行单元,栈帧则是用于支持虚拟机进行方法调用和方法执行背后的数据结构。每一个方法从调用开始到执行结束的过程,都对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程。

运行时栈帧结构

每一个栈帧由局部变量表操作数栈固定帧(桢数据)组成。其中,固定帧还包括方法指令常量池调用方栈底返回地址等信息。

每一个线程中的方法调用链可能会很长,所以一个线程中会包含多个栈帧。在活动的线程中,只有栈顶的方法才是在运行的,只有位于栈顶的栈帧才是生效的,这个位于栈顶的栈帧叫当前栈帧

线程栈帧结构.png 栈帧结构.png

操作数栈

栈顶缓存

栈帧重叠

局部变量表

局部变量表是一组变量值的存储空间,用于存放方法参数和方法内部定义的局部变量。局部变量表的容量以变量槽为最小单位。当JVM准备调用Java方法时,会为该方法创建栈帧,而栈帧中就包括局部变量表所需的空间,由于局部变量表建立在线程的堆栈空间中,因此是线程的私有数据,不会有线程安全问题。

局部变量表中第0位索引的变量槽存储该实力方法所在对象的引用,即this关键字,后续的其他参数将会传递至局部变量表中从1开始的连续位置上。所以,在非静态方法中,locals的数量至少为1,就是这个this。

局部变量表.png

栈深与slot复用

上面说到每一个Java方法栈帧由3部分组成:局部变量表操作数栈固定帧。由于固定帧的大小是固定不变的,因此Java方法栈帧的大小取决于局部变量表和操作数栈的大小,而由于栈帧重叠,可以粗略的认为操作数栈属于被调用者方法的栈帧的一部分,由此推断出,一个Java方法的栈帧大小主要取决于局部变量表的大小。当JVM设定默认的堆栈空间大小后-Xss,一个Java线程所能调用的最大方法深度便直接取决于Java方法局部变量表的大小。所以,如果局部变量表所占空间大,则线程所能调用的最大方法的深度便会变小。

(此处应该验证)

由于局部变量表的大小会直接影响一个线程所能调用的方法深度,所以一个优化手段便是,使slot能够复用。slot复用能相对的减少局部变量表的大小,从而提高线程调用的最大方法深度。

(此处应该验证)

动态链接

每一个栈帧都包含和一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态链接。Class文件常量池中的符号引用,会分为两部分解析:一部分是静态解析,是在类加载阶段时替换为直接引用;一部分是动态解析,将在每一次运行期间替换为直接直接,而动态解析这部分就是动态链接。

实现动态链接的关键便是在Java方法栈中持有一个指针,指向常量池,就能得到该Java方法的字节码指令,并根据字节码指令映射到机器指令,从而完成方法逻辑处理。

相关文章

  • JVM-从字节码到运行时(2)

    JVM-从字节码到运行时(2) 基于栈的解释器执行过程 这是例子ByteCodeDemo类中的add(int z)...

  • JVM-从字节码到运行时(1)

    JVM-从字节码到运行时(1) 一切从javap -verbose开始 希望借此文章将Class文件结构和运行时的...

  • DVM执行 java 程序的工具

    jvm 执行字节码原理:java 程序运行时,是由一个 java 虚拟机来解释 java 字节码的,它将这些字节码...

  • JVM系列:Java内存区域与内存溢出异常

    运行时数据区域 程序计数器 作用记录当前线程所执行到的字节码的行号。字节码解释器工作的时候就是通过改变这个计数器的...

  • 面试问题汇总

    2017-3-22 腾讯 1、如何运行时修改Java字节码?参考Java动态编程初探——Javassist 2、如...

  • 【JAVA】JAVA中的小知识(补充中...)

    Java文件经过JVM编译成字节码文件,即.class文件,将字节码文件在不同的操作系统中运行时,操作系统再将字节...

  • 代码生成

    语法解析流程简述 lua之类的脚本语言先把源代码翻译成字节码,而后再使用虚拟机执行字节码。而从源代码到字节码要经过...

  • 深入理解java虚拟机读书笔记,第八章:虚拟机字节码执行引擎

    8.1概述 执行引擎:输入字节码文件,处理过程是字节码解析的等效过程,输出的是执行结果 8.2运行时栈帧结构 栈帧...

  • JVM

    1 JVM 运行时内存区域划分ans: (1)程序计数器:这部分空间用于保存当前线程所执行的字节码的行号,字节码解...

  • 2021-06-05 动态代理原理

    运行时生成代理类的字节码代理对象持有InvocationHandler的对象,InvocationHandler的...

网友评论

      本文标题:JVM-从字节码到运行时(2)

      本文链接:https://www.haomeiwen.com/subject/msfmlltx.html