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符号地址,转化为内存中的地址。

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • PAT乙级1013

    PAT乙级1013实现#include&lt;iostream&gt;#include&lt;cmath&gt;usingnamespacestd;boolisPrime(intnum){ inti; for(i=2;i&lt;=sqrt(num);i++) { if(num%i==0) returnfalse; } returntrue;…

  • java输出日期格式_java时区转换

    java输出日期格式_java时区转换importorg.joda.time.DateTime;importjava.util.Date;publicvoidsetCreatedDate(DatecreatedDate){this.createdDate=createdDate;}publicvoidsetCreatedDate(finalDateTimecreatedDate){this.createdDa…

    2022年10月24日
  • 解决SQLyog连接MySQL8时报错:错误号码2058

    解决SQLyog连接MySQL8时报错:错误号码2058远在天边,近在眼前。

    2022年10月23日
  • strtok独到深刻的讲解「建议收藏」

    strtok独到深刻的讲解「建议收藏」strtok函数的使用是一个老生常谈的问题了。该函数的作用很大,争议也很大。以下的表述可能与一些资料有区别或者说与你原来的认识有差异,因此,我尽量以实验为证。交代一下实验环境是必要的,winxp+vc6.0,一个极端平民化的实验环境。本文中使用的源代码大部分来自于网络,我稍加修改作为例证。当然,本人水平有限,有不妥之处在所难免,各位见谅的同时不妨多做实验,以实验为证。strtok的

  • 十款磁盘碎片整理工具

    十款磁盘碎片整理工具说到磁盘整理工具,应该说说磁盘碎片的定义,为什么磁盘碎片会对系统性能造成影响。首先我不是专业的电脑人员,对很专业的理论知识不懂,在这里只可以用很通俗很日常的语言来表达。其实磁盘碎片应该称为文件碎片,是因为文件被分散保存到整个磁盘的不同地方,而不是连续地保存在磁盘连续的簇中形成的。为什么这些碎片多了,会对系统性能造成影响呢?打个比方,你的房间你很久没有整理和清洁了,原本有条…

  • html二级菜单的创建[通俗易懂]

    html二级菜单的创建[通俗易懂]二级菜单用的是无序列表嵌套,:hover鼠标悬浮其上方发生的事<!DOCTYPEhtml><html> <head> <metacharset=”utf-8″/> <title></title> <styletype=”text/css”> *{margin:0; …

发表回复

您的电子邮箱地址不会被公开。

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