【Java 虚拟机原理】栈帧 | 动态链接 | 方法区 | 字节码文件二进制分析

举报
韩曙亮 发表于 2022/01/13 22:40:43 2022/01/13
【摘要】 文章目录 前言一、方法区二、字节码二进制文件分析三、动态链接1、动态链接简介2、静态链接与动态链接3、早期绑定 和 晚期绑定4、动态链接示例 前言 " 栈帧 " 中存储的是 局部变量表...

前言

" 栈帧 " 中存储的是 局部变量表 , 操作数栈 , 动态链接 , 方法出口 ;





一、方法区



字节码文件加载到内存中后 , 该文件的 Class 会存放到 方法区 ( 元空间 ) 中 ; 方法区 中存储 静态变量 , 常量 , 类元信息 ;

类元信息 是由 方法和数据组成 ;


如果定义了一个静态变量类对象 , 那么方法区中 , 的该静态变量 指向了 堆 中的对象 ;

public static HelloWorld mHelloWorld = new HelloWorld();

  
 
  • 1

如果在 方法的局部变量 中创建了 类对象 , 那么 线程栈 中的局部变量 , 也会指向 堆 中的对象 ;

    public static void main(String[] args) {
        HelloWorld helloWorld = new HelloWorld();
        helloWorld.add();
    }

  
 
  • 1
  • 2
  • 3
  • 4

类 的 实例对象 创建完成后 , 除了在类中封装的成员之外 , 还包括 " 对象头 " ( Object Header ) ,

对象头 中包含 3 3 3 部分内容 :

  • 数据区 ;
  • Marker Word 表及字段 ;
  • KlassPointer 类型指针 , 指向 方法区 ( 元空间 ) 中的 类元信息 的地址 ;





二、字节码二进制文件分析



Java 源代码如下 :

public class HelloWorld {

    public static HelloWorld mHelloWorld = new HelloWorld();

    public int add() {
        int a = 1;
        int b = 1;
        int c = a + b;
        return c;
    }

    public static void main(String[] args) {
        HelloWorld helloWorld = new HelloWorld();
        helloWorld.add();
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

字节码文件二进制数据分析 :

使用二进制查看工具查看 HelloWorld.class 字节码文件 , 这些二进制数值对应的就是 JVM 指令 ;

在这里插入图片描述

执行

javap -v HelloWorld.class 

  
 
  • 1

命令 , 命令行终端输出的就是字节码二进制数据的翻译内容 ;

major version: 52 配置 JDK 版本 ;

Constant pool 常量池 ;

D:\java>javap -v HelloWorld.class
Classfile /D:/java/HelloWorld.class
  Last modified 2021-9-2; size 373 bytes
  MD5 checksum a9899195af11ef123968f811f4aa71f4
  Compiled from "HelloWorld.java"
public class HelloWorld
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #5.#16         // java/lang/Object."<init>":()V
   #2 = Class              #17            // HelloWorld
   #3 = Methodref          #2.#16         // HelloWorld."<init>":()V
   #4 = Methodref          #2.#18         // HelloWorld.add:()I
   #5 = Class              #19            // java/lang/Object
   #6 = Utf8               <init>
   #7 = Utf8               ()V
   #8 = Utf8               Code
   #9 = Utf8               LineNumberTable
  #10 = Utf8               add
  #11 = Utf8               ()I
  #12 = Utf8               main
  #13 = Utf8               ([Ljava/lang/String;)V
  #14 = Utf8               SourceFile
  #15 = Utf8               HelloWorld.java
  #16 = NameAndType        #6:#7          // "<init>":()V
  #17 = Utf8               HelloWorld
  #18 = NameAndType        #10:#11        // add:()I
  #19 = Utf8               java/lang/Object
{
  public HelloWorld();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public int add();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=1
         0: iconst_1
         1: istore_1
         2: iconst_1
         3: istore_2
         4: iload_1
         5: iload_2
         6: iadd
         7: istore_3
         8: iload_3
         9: ireturn
      LineNumberTable:
        line 4: 0
        line 5: 2
        line 6: 4
        line 7: 8

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: new           #2                  // class HelloWorld
         3: dup
         4: invokespecial #3                  // Method "<init>":()V
         7: astore_1
         8: aload_1
         9: invokevirtual #4                  // Method add:()I
        12: pop
        13: return
      LineNumberTable:
        line 11: 0
        line 12: 8
        line 13: 13
}
SourceFile: "HelloWorld.java"

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81

在这里插入图片描述





三、动态链接




1、动态链接简介


动态链接 又称为 运行时常量池方法引用 ;

每个 方法 都有一个对应的 " 栈帧 " , 在栈帧 内部 的 " 动态链接 " 中 , 包含了 " 运行时常量池 " 中 栈帧对应方法的引用 , 该操作的目的是支持当前 方法实现 动态链接 ;


2、静态链接与动态链接


静态链接 : 字节码文件加载到 Java 虚拟机内存后 , 如果在 编译阶段 就知道 目标方法 的 引用 , 并且在 运行时引用不变 , 那么调用方法时 , 直接使用 方法的符号引用 转为 直接引用 的过程 , 称为 静态链接 ;

动态链接 : 编译阶段 , 无法确定 被调用的方法 , 只能在 运行时方法符号引用 转为 直接引用 , 这种 动态的引用转换 , 称为 动态链接 ;


3、早期绑定 和 晚期绑定


方法绑定 分为 早期绑定 和 晚期绑定 ;

早期绑定 : 被调用的方法在 编译期 可以知道 , 并且运行时保持不变 , 静态链接 ;

晚期绑定 : 被调用的方法 在 编译期 无法确定 , 在运行时动态地绑定相关方法 , 动态链接 ;


4、动态链接示例


动态链接指的是 , 将 Java 源码编译为 class 字节码文件后 , 方法调用 如 helloWorld.add() , 在 class 字节码文件中只是一个字符 , 在运行时 , 需要靠 " 动态链接 " 指向要运行的 helloWorld.add() 方法首地址 ;

" 动态链接 " 本质是 将 " 符号引用 " 转为 " 直接引用 " ;


在上述字节码 常量池 中 HelloWorld 类的 add 方法的引用如下 :

#4 = Methodref          #2.#18         // HelloWorld.add:()I

  
 
  • 1

#4 = Methodref 指向了 #2.#18 , #18 就是 add 方法 ;

  #18 = NameAndType        #10:#11        // add:()I

  
 
  • 1

文章来源: hanshuliang.blog.csdn.net,作者:韩曙亮,版权归原作者所有,如需转载,请联系作者。

原文链接:hanshuliang.blog.csdn.net/article/details/120060894

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。