JVM常量池和运行时常量池「建议收藏」

JVM常量池和运行时常量池「建议收藏」一、类的二进制字节码包含哪些信息要理解常量池是什么,先看看类的二进制字节码包含哪些信息!!!常量池类的基本信息(比如:类的访问权限、类的名称、实现了哪些接口)类的方法定义(包含了虚拟机指令,也就是把我们代码编译为了虚拟机指令)二、通过反编译字节码验证1、测试代码将下面的测试代码使用javac编译为*.class文件publicclassHelloWorld{publicstaticvoidmain(String[]args){System

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

一、类的二进制字节码包含哪些信息

要理解常量池是什么,先看看类的二进制字节码包含哪些信息!!!

  • 常量池
  • 类的基本信息(比如:类的访问权限、类的名称、实现了哪些接口)
  • 类的方法定义(包含了虚拟机指令,也就是把我们代码编译为了虚拟机指令 )

二、通过反编译字节码验证

1、测试代码

将下面的测试代码使用javac 编译为 *.class文件

public class HelloWorld { 
   
    public static void main(String[] args) { 
   
        System.out.println("hello world");
    }
}

2、javap反编译*.class字节码

先将示例代码编译为 *.class 文件,然后将class文件反编译为JVM指令码。然后观察 *.class字节码中到底包含了哪些部分。

// ===========================================类的描述信息===============================================
Classfile /xx/xx/xx/xx/HelloWorld.class
  Last modified 2021-10-12; size 569 bytes
  MD5 checksum 7f4f0fe4b6e6d04ddaf30401a7b04f07
  Compiled from "HelloWorld.java"
public class org.memory.jvm.t5.HelloWorld
  minor version: 0
  major version: 49
  flags: ACC_PUBLIC, ACC_SUPER
    
// ===========================================常量池===============================================
Constant pool:
   #1 = Methodref          #6.#20         // java/lang/Object."<init>":()V
   #2 = Fieldref           #21.#22        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #23            // hello world
   #4 = Methodref          #24.#25        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #26            // org/memory/jvm/t5/HelloWorld
   #6 = Class              #27            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Lorg/memory/jvm/t5/HelloWorld;
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               args
  #17 = Utf8               [Ljava/lang/String;
  #18 = Utf8               SourceFile
  #19 = Utf8               HelloWorld.java
  #20 = NameAndType        #7:#8          // "<init>":()V
  #21 = Class              #28            // java/lang/System
  #22 = NameAndType        #29:#30        // out:Ljava/io/PrintStream;
  #23 = Utf8               hello world
  #24 = Class              #31            // java/io/PrintStream
  #25 = NameAndType        #32:#33        // println:(Ljava/lang/String;)V
  #26 = Utf8               org/memory/jvm/t5/HelloWorld
  #27 = Utf8               java/lang/Object
  #28 = Utf8               java/lang/System
  #29 = Utf8               out
  #30 = Utf8               Ljava/io/PrintStream;
  #31 = Utf8               java/io/PrintStream
  #32 = Utf8               println
  #33 = Utf8               (Ljava/lang/String;)V
                            
// =======================================虚拟机中执行编译的方法===========================================
{ 
   
  public org.memory.jvm.t5.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 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lorg/memory/jvm/t5/HelloWorld;
	
  // main方法JVM指令码
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    // main方法访问修饰符描述
    flags: ACC_PUBLIC, ACC_STATIC
    // main方法中的代码执行部分
    // ===============================解释器读取下面的JVM指令解释并执行=================================== 
    Code:
      stack=2, locals=1, args_size=1
         // 从常量池中符号地址为 #2 的地方,先获取静态变量System.out
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         // 从常量池中符号地址为 #3 的地方加载常量 hello world
         3: ldc           #3                  // String hello world
         // 从常量池中符号地址为 #3 的地方获取要执行的方法描述,并执行方法输出hello world
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         // main方法返回
         8: return
    // ==================================解释器读取上面的JVM指令解释并执行================================
      // 行号映射表
      LineNumberTable:
        line 9: 0
        line 10: 8
      // 局部变量表
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  args   [Ljava/lang/String;
}

三、什么是常量池以及常量池的作用

1、什么是常量池

从上面的反编译字节码中可以看到,Class的常量池其实就是一张记录着该类的一些常量、方法描述、类描述、变量描述信息的表。

2、常量池中有什么内容

常量池中主要存放两类数据,一是字面量、二是符号引用

字面量:

  • 比如String类型的字符串值或者定义为final类型的常量的值。

符号引用:

  • 类或接口的全限定名(包括他的父类和所实现的接口)
  • 变量或方法的名称
  • 变量或方法的描述信息
  • this

可参考:https://blog.csdn.net/Hellowenpan/article/details/101389330

3、常量池的作用

在解释器解释执行每条JVM指令码的时候,根据这些指令码的符号地址去常量池中找到对应的描述。然后解释器就知道该执行哪个类的那个方法、方法的参数是什么等。

拿上面反编译的字节码指令来说明:

  1. 当解释器解释执行main方法的时候,读取到下面的11行JVM指令码0: getstatic #2
  2. getstatic指令表示获取一个静态变量,#2表示该静态变量的符号地址,解释器通过#2符号地址去常量池中查找#2对应的静态变量
  3. 然后解释器继续向下运行,执行第13行的3: ldc #3指令,该指令的含义是:从常量池中加载符号地址为 #3 的常量
  4. 然后解释器继续向下运行,执行第15行的5: invokevirtual #4指令,该指令的含义是:执行方法,那么要执行哪个方法呢?执行常量池中符号地址为 #4 的方法。
  // main方法JVM指令码
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    // main方法访问修饰符描述
    flags: ACC_PUBLIC, ACC_STATIC
    // main方法中的代码执行部分
    // ===============================解释器读取下面的JVM指令解释并执行=================================== 
    Code:
      stack=2, locals=1, args_size=1
         // 从常量池中符号地址为 #2 的地方,先获取静态变量System.out
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         // 从常量池中符号地址为 #3 的地方加载常量 hello world
         3: ldc           #3                  // String hello world
         // 从常量池中符号地址为 #3 的地方获取要执行的方法描述,并执行方法输出hello world
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         // main方法返回
         8: return
    // ==================================解释器读取上面的JVM指令解释并执行================================

四、运行时常量池

1、什么是运行时常量池

上面我们分析了常量池其实就是一张对照表,常量池是 *.class 文件中的。当类的字节码被加载到内存中后,他的常量池信息就会集中放入到一块内存,这块内存就称为运行时常量池,并且把里面的符号地址为真实地址

2、符号地址变为真实地址怎么理解

①、符号地址

从上面的反编译后的JVM字节码指令可以看到有这么一条指令0: getstatic #2,解释器解释执行JVM指令的时候,通过指令中的 #x去常量池中获取需要的值。这里的#2其实就是符号地址,标识这某个变量在常量池中的某个位置。

②、真实地址

在程序运行期,当*.Class文件被加载到内存以后,常量池中的这些描述信息就会被放到内存中,其中的 #x会被转化为内存中的地址(真实地址)。

③、简单总结

符号地址变为真实地址其实就是,在*.class文件被加载到内存以后,将*.class文件中常量池中的#x符号地址,转化为内存中的地址。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/195808.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • pycharm输出中文出现乱码的几种解决方法以及读取时打印出现乱码的解决[通俗易懂]

    pycharm输出中文出现乱码的几种解决方法以及读取时打印出现乱码的解决[通俗易懂]pycharm打印中文出现乱码,有几种情况第一种:对于这种情况,是普通的一种,你需要检查开头,是否加了#-*-coding:utf-8-*-还有importsysreload(sys)sys.setdefaultencoding(‘utf-8’)对于这两个处理中文输出的代码,最好每次处理中文时就加上,会减少很多问题第二种问题:

    2022年8月28日
    3
  • Mysql decimal详解

    Mysql decimal详解Mysqldecimal如何定义特点特点一详解如何定义decimal(totalCount,afterCount)参数说明totalCount:数字数量总和afterCount:小数点后数字数量总和特点每4字节存储9位数字,不足9位试具体位数决定占用空间最多存储65位数字,其中小数点前最多占35个,小数点后最多占30小数点占一字节最大空间占用为31字节特点一详解12(9+3)位数字,占6(4+2)位,3位数字最大值为999,1字节(28,256)<999

    2022年7月17日
    17
  • PID为0的系统空闲进程连接状态为TIME_WAIT

    PID为0的系统空闲进程连接状态为TIME_WAIT如果您使用命令提示符执行命令查看网络连接情况,您会发现,PID为0的SystemIdleProcess(系统空闲进程)将会出现很多网络端口占用情况。下面是一个示例:Proto LocalAddress     &nbs…

    2022年10月20日
    0
  • linux mysql 修改表名_Linux下mysql怎么设置表名?「建议收藏」

    linux mysql 修改表名_Linux下mysql怎么设置表名?「建议收藏」Linux下mysql可以通过“ALTERTABLE旧表名RENAME[TO]新表名;”语句来修改表名;还可以通过配置my.cnf文件,修改“lower_case_table_names”选项的值为“1”来设置表名不区分大小写。Linux下mysql设置表名在MySQL中,可以使用ALTERTABLE语句来实现表名的修改。在MySQL中可以使用ALTERTABLE语句…

    2022年5月31日
    29
  • 谈谈我对画面撕裂,垂直同步,Freesync以及G-sync的理解「建议收藏」

    谈谈我对画面撕裂,垂直同步,Freesync以及G-sync的理解「建议收藏」最近一直在接触图形学相关的知识,感觉之前在学OpenGL的时候不需要思考帧缓冲是怎么处理到显示器上的,驱动都帮我做好了,现在在接触vulkan的时候发现自己对Swapchain这个东西的工作原理不是很了解,去网上搜索资料的过程中发现了垂直同步这个知识点,以前玩游戏的时候也经常看到但是不明白什么意思(对不起!我不是一个合格的游戏玩家>-<),觉得自己还是得搞清楚一下,于是整理了一下自己对…

    2022年6月10日
    44
  • Akka FSM 源代码分析

    Akka FSM 源代码分析

    2022年1月1日
    55

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号