.net core 字符串取消符号_「面向 Boss 编程」JVM 链接阶段之符号引用与直接引用...

JVM 架构简图

JVM 的类加载子系统(Class Load SubSystem) 是 JVM 架构的第一个重要阶段,我们最常见的 JVM 架构简图是下面这样事儿的,只瞅一眼就跟明镜儿似的,类加载子系统的责任那是明明白白的,负责从文件系统或网络中加载 class 文件到 JVM 运行时数据区。

cd62af001ae208b17f0a742d27db5861.png

JVM 架构简图

类的加载过程

既然都这么明确了,那只能不绕圈子了,其实类加载子系统还是有点复杂的,并不像上图那样跟一个青春妹子一样单纯、可爱、简单,她的内心你得多少了解点,不然我讲不下去了,想留住单纯美好时光的肥宅们,可以到此打住了。

类加载子系统虽然只负责将 class 文件加载到 JVM 的运行时数据区,其实她也不管最后能不能执行,反正是符合她的“择偶”条件就会加载,虽然单纯可爱简单,但是该有的流程还是要有的。

有好事者总结如下:

  1. 加载(Loading): 在内存中生成一个代表该 class 类的 java.lang.Class 对象
  2. 链接(Linking): 其实这个阶段还分为验证、准备、解析三个阶段
  3. 初始化(Resolution):执行类构造器方法 () 完成初始化操作
f198c4872314211156cfcc6396be3585.png

类加载子系统

还是有点小复杂,但今天不能重点介绍,说多了就没有距离感了,还是先保留一点单纯可爱简单吧。

接下来说今天要探讨的重点:链接(Linking)阶段的解析过程(Resolution)

链接(Linking)阶段

链接(Linking)阶段分为了三个阶段:验证(Verification)、准备(Preparation)、解析(Resolution)。

验证阶段,简单来说,确保 class 文件包含的信息是符合当前虚拟机要求的,确保加载类的正确性,确保其不会危害虚拟机的自身安全;这个阶段主要包括四种验证:文件格式验证、元数据验证、字节码验证、符号引用验证。

著名的魔数【CAFEBABE】(谐音:咖啡宝贝)就是在这个阶段验证的,凡是 JVM 标准的 class 文件都是以这个魔数开始的,名字听着就那么个感觉!

准备阶段,简单来说,就是类变量分配内存并且设置该类的默认初始值,也就是零值;比如定义了一个 static 变量 a = 1,在这个阶段会默认赋值 a = 0,在后面的初始化阶段会将 a 赋值为 1。

需要注意的是:

1. 这个阶段不包含用 final 修饰的 static 变量,因为 final 会在编译阶段分配

2. 这个阶段不包含实例变量的分配与初始化,因为它们会在堆区创建对象时初始化

好了,终于到整体了,接下来就是解析(Resolution)阶段啦。

解析阶段,简单来说,就是常量池中的符号引用转换为直接引用的过程,解析的主要是类或接口、字段、类方法、接口方法、方法类型等,对应到常量池中的CONSTANT_Class_info、 CONSTANT_Fieldref_info、 CONSTANT_Methodref_info 等。

千呼万唤,终于出来了,接下来就是本篇核心知识点了:符号引用、直接引用了。

符号引用与直接引用

解析阶段的主要操作是将类常量池中的符号引用替换为直接引用。啥?(黑人问号脸)

符号引用,简单说,就是一个字符串,这个字符串定义了被引用项的唯一识别信息,根据这个信息可以无歧义地定位到一个类、字段、方法等。

一句话:就是一个唯一标识符

直接引用,简单说,就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。

一句话:就是一个直接内存地址的表示

为什么会有符号引用?

符号引用与虚拟机的内存布局无关,引用的目标并不一定加载到内存中,一个 java 类编译为 class 文件时,java 类并不知道所引用的类的实际地址,因此只能使用符号引用代替。

初始化阶段后,已经可以知道被引用项的真实地址(也就是,直接引用)了,解析阶段会将常量池内的符号引用替换为直接引用。

各种虚拟机实现的内存布局可能有所不同,但是它们能接受的符号引用都是一致的,因为符号引用的字面量形式明确定义在 Java 虚拟机规范的 Class 文件格式中。

Boss:来一个小示例

Boss 说太干了,来点实际的,要想成功就得让我听懂。

有好事者总结了:

(1)我们先写个 java 版本的 Hello World 小程序(PS :这个 Boss 再不懂就辞职啦)

public class Hello {    private static int a = 1;      public static void main(String[] args) {        System.out.println(a);    }}       

(2)写完后我们编译一下,有 IDE 的直接自动编译,没有的使用 javac 搞一下

(3)编译成功后可以找到 Hello.class,哎,对就是这个!

(4)然后在 class 文件所在目录打开命令行,输入一个神奇命令:javap -v Hello.class

(5)接下来,见证一下奇迹,找寻一下常量池(Constant Pool)

Boss,请看下图:

这就是编译后常量池中定义的符号引用了,看看 Ljava/lang/String; 就是代表 java.lang.String 类。

f12e308322aefd77d26afa2784da155c.png

常量池的符号引用

符号引用的常见描述符

本着服务 Boss 的宗旨,这么多描述符,鬼知道是怎么回事啊,Boss 教导说,工作要有总结。

那么,好事者又总结了。

JNI 字符描述符是类描述符、方法描述符、字段描述符的具体的实现规则,描述规则如下:

(1)类描述符 : 以 L 开头,其后跟着该类的全限定名,并将其中中的 “.” 改为 “/” ,最后分号“;”结束

 类:Java.lang.String 对应描述符:Ljava/lang/String;

(2)基本类型描述符:基本类型不存在全限定名,故只需要一个符号就可以表达该基本类型

boolean Zbyte    Bchar    Cshort   Sint     Ilong    Jfloat   Fdouble  Dvoid    V

(3)字段描述符 :格式为 字段名:类型

 int num = 3; 字段描述符: num:I 字段名: num

(4)数组描述符 : 原描述符前加一个 [ 表示一维数组,加两个[[ 表示二维数组

 String[][] num=null; 字段描述符:[[Ljava/lang/String; 字段名:num

(5)方法描述符: (第一个参数的字段描述符第二个参数的字段描述符.....)返回类型的描述符

 # 示例 1 String test(); 方法描述符:()Ljava/lang/String; 方法名:test  # 示例 2 long test(int i, Object c); 方法描述符:(ILjava/lang/Object;)J 方法名:test  # 示例 3 void test(byte[] bytes); 方法描述符:([B)V 方法名:test

Boss:来一个小总结

Boss 说得对,来个小总结吧,好事者又开始了表演。

  1. 类加载子系统是 JVM 运行数据的第一个阶段,这个阶段是这种的目标是将 class 文件加载到运行时数据区,创建出对应的 java.lang.Class 对象;
  2. 类加载子系统的加载过程又包括三个大阶段:加载、链接、初始化;
  3. 加载阶段的作用是在内存中生成一个代表这个类的 java.lang.Class 对象
  4. 链接阶段的作用是完成 class 文件的验证、分配内存并赋默认值、最后替换常量池的符号引用,也就是验证、准备、解析三个阶段;
  5. 符号引用就是一个唯一标识被引用项的字符串,直接引用就是一个直接内存地址的表示,可以是指向目标的指针、相对偏移量或一个间接定位目标的句柄;
  6. 之所以会有符号引用,是因为在编译阶段,并不知道变量的真实地址(也就是直接引用),而且每个虚拟机的实现各有不同,因此需要前期定义,后期再进行替换,也就是上面的解析阶段;
  7. 符号引用在 java 虚拟机规范的 Class 文件格式中有明确定义,JNI 字符描述符是类描述符、方法描述符、字段描述符等描述符的具体的实现规则。

参考资料

感谢以下文章或视频提供的学习资料,受益匪浅:

  1. 尚硅谷《宋红康-JVM 从入门到精通》课程 : http://www.atguigu.com/
  2. java -- JVM的符号引用和直接引用 : https://www.cnblogs.com/shinubi/articles/6116993.html
  3. JVM 符号引用(Symbolic References) : https://blog.csdn.net/u014296316/article/details/83066436

写在最后的话

内容为学习相关资料后的总结,一字一码,姑且算是原创吧,欢迎转载,公众号或商业转载请联系本人。

谢谢各位读完本文,初涉写作,内容不佳,敬请见谅,如果对您有帮助,欢迎转发、评论、关注。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值