8.1 动态连接和解析

Class文件把它所有的引用符号保存在一个地方——常量池。每一个class文件有一个常量池,每一个被Java虚拟机装载的类或者接口都有一份内部版本的常量池,被称作运行时常量池。运行时常量池是一个特定于实现的数据结构,数据结构映射到class文件中的常量池。因此当一个类型被首次装载时,所有来自于类型的符号引用都装载到了类型的运行时常量池。

当程序运行到某个时刻,如果某个特定的符号引用将要被使用,它首先要被解析。解析过程就是根据符号引用查找到实体,再把符号引用替换成一个直接引用的过程。因为所有的符号引用都保存在常量池中,所以这个过程常被称作常量池解析。

记住:java虚拟机为每一个装载的类和接口保存一份独立的常量池。

8.1.1解析和动态扩展

Java的体系结构允许动态扩展Java程序,这个过程包括运行时决定所使用的类型,装载它们,使用它们。通过传递类型的名字到java.lang.Class的forName()方法,或者用户自定义的类装载器的loadClass()方法,可以动态扩展Java程序。动态扩展的两种方式:

1)  直接使用java.lang.Class的forName()方法

Public static Class forName(String className)throwsClassNotFoundException;

//该方法使用当前类的类装载器,并且总是初始化该类型

Public satic Class forName(String className,Booleaninitialize,ClassLoader loader)throws ClassNotFoundException;

//initialize参数可以指定是否在装载完后进行初始化,loader可以指定装载的类装载器。

2)  使用用户自定义的类装载器的loadClass()方法

如果需要用自定义的类装载器请求类型,只需要调用那个类装载器的loadClass()方法。

Protected Class loadClass(String name)throws ClassNotFoundException

Protected Class loadClass(String name,Boolean resolve)throwsClassNotFoundException;

两个loadClass()方法都接受装载类型的全限定名装入String类型的name参数。双参数版本的loadClass()中,boolean类型的参数表示是否在装载时执行该类型的连接。

区别:如果没有特别要使用类装载器的要求,应该用forName(),如果需要请求的类型在装载时就初始化的话,则不得不使用forName();如果需要一些特定的装载类型的方法,比如从网络上下载,从数据库中取出,从加密文件中提取,甚至动态地创建它们,这时就需要一个类装载器。

8.1.4 解析CONSTANT_Class_info入口

常量池入口类型中,解析起来最复杂的就是CONSTANT_Class_info了。

数组类的解析:

每一个数组在虚拟机中都会被解析成一人Class实例,如果数组的元素类型是一个引用类型,虚拟机用当前类装载器解析元素类型。如果数组的元素类型是一个基本类型,那么虚拟机立即创建关于那个元素类型的新数组类,维数也在此时确定,然后创建一个Class的实例来代表这个类型。如果是关于引用的数组,数组会标记为是由定义它的元素类型的类装载器定义的。如果是关于基本类型的数组,数组类会被标记为是由启动类装载器定义的。

非数组类和接口的解析:

由于需要分多步来解析,下面以1a到2d来说明这一过程。

步骤1:作为解析的第一步,虚拟机必须确定是否被引用的类型已经被装载进了当前命名空间,如果没有被装载进当前命名空间,虚拟机把类型的全限定名传递给当前类装载器。

步骤1a:装载类型或者任何超类型

对于每一个类装载器,Java虚拟机维护一张列表,其中记录了所有其装载的类型的名字。每一张这样的列表就组成了java虚拟机内部的命名空间。虚拟机会使用双亲委派模型来装载类型,一旦被引用的类型被装载了,虚拟机仔细检查它的二进制数据。如果类型是一个类,并且不是java.lang.Object,虚拟机根据类的数据得到它的直接超类的全限定名。虚拟机接着会察看超类是否已经被装载进当前命名空间了。如果没有,先装载超类。一旦超类被装载了,虚拟机再次检查它的二进制数据来找到它的超类。一直重复到超类为Object为止。然后在从Object返回的路上,虚拟机再次检查每个类型是否直接实现了任何接口,如果这样,它会确保那些接口也被装载了。经过步骤1a,java虚拟机确认某个类型是否被装载了,并确保它的所有超类和所有超接口都被装载了。

步骤1b:检查访问权限

如果发起引用的类型没有访问被引用的类型的权限,虚拟机抛出IllegalAccessError异常。检查访问权限是在正式校验阶段之前进行的。

步骤2:连接并初始化类型和任何超类

步骤2a:校验类型

这一步就是第七章中的正式校验阶段,校验阶段可能要求虚拟机装载新的类型来确认字节码符合java语言的语义。

步骤2b:准备类型

在准备阶段虚拟机为类型变量以及随实现不同而有差别的数据结构(如方法表)分配内存。

步骤2c:可选的步骤,解析类型

步骤1a、2a、2b已经解析了发起引用的类型的常量池的CONSANT_Class_info入口。步骤2c是关于被引用类型中所包含的符号引用的解析。

步骤2d:初始化类型

如果类型拥有超类,初始化类型的超类是按自顶向下的顺序进行的。

8.1.5解析CONSTANT_Fieldref_info入口

要解析类型是CONSTANT_Fieldref_info的常量池入口,虚拟机必须首先解析class_index项中指明的CONSTANT_Class_info入口。如果CONSTANT_Class_info解析成功,虚拟机按照如下步骤执行字段搜索过程:

1)  虚拟机在被引用的类型中查找具有指定的名字和类型的字段。如果虚拟机找到了这样一个字段,这个字段就是成功的字段搜索结果。

2)  否则,虚拟机检查类型直接实现或扩展的接口,以及递归地检查它们的接口。如果找到名字和类型都符合的字段,这个字段就是成功的字段搜索结果。

3)  否则,如果类型拥有一个直接的超类,虚拟机检查类型的直接超类,并且递归地检查类型的所有超类,如果找到了名字和类型都符合的字段,这个字段就是成功的字段搜索结果。

4)  字段搜索失败。

如果字段搜索到,虚拟机把这个入口标记为已解析,并在这个常量池入口的数据中放上指向这个字段的直接引用。

8.1.6解析CONSTANT_Methodref_info入口

要解析类型是CONSTANT_Methodref_info的常量池入口,虚拟机必须首先解析class_index项中指明的CONSTANT_Class_info入口。如果CONSTANT_Class_info解析成功,虚拟机按照如下步骤执行方法解析:

1)  如果被解析的类型是一个接口,而非类,虚拟机抛出IncompatibleClassChangeError

2)  否则,虚拟机检查被引用的类是否有一个方法符合指定的名字以及描述符。如果虚拟机找到了这样一个方法,这个方法就是成功的方法搜索结果。

3)  否则,如果类型拥有一个直接的超类,虚拟机检查类型的直接超类,并且递归地检查类型的所有超类,查找是否有一个方法符合指定的名字以及描述符,如果找到了这样一个方法,这个方法就是成功的字段搜索结果。

4)  否则,虚拟机检查类型直接实现或扩展的接口,以及递归地检查它们的接口。查找是否有一个方法符合指定的名字以及描述符,如果找到了这样一个方法,这个方法就是成功的字段搜索结果。

5)  否则,方法搜索失败。

如果方法搜索到,虚拟机把这个入口标记为已解析,并在这个常量池入口的数据中放上指向该方法的直接引用。

8.1.8解析CONSTANT_String_info入口

每一个java虚拟机必须维护一张内部列表,它列出了所有在运行程序的过程中已被“拘留(intern)”的字符串对象的引用。基本上,如果一个字符串在虚拟机的拘留列表上出现,就说它被拘留了。维护这个列表的关键是任何特定的字符序列在这个列表上都只出现一次。

要拘留CONSTANT_String_info入口所代表的字符序列,虚拟机要检查内部拘留名单上这个字符序列是否已经在编了。如果已经在编,虚拟机使用指向以前拘留的字符串对象的引用。否则虚拟机按照这个字符序列创建一个新的字符对象,并把这个对象的引用编入列表。

在Java程序中,可以调用String类的intern()方法来拘留一个字符串。

8.1.11编译时常量解析

被初始化为编译时常量的静态final变量的引用,在编译时被解析为常量值的一个本地拷贝,这对所有基本类型和java.lang.String都是正确的。

有两个好处:1)常量值的本地拷贝使得静态final变量可以用于switch语句中的case表达式。2)条件编译。

java虚拟机学习笔记——连接模型(第八章)相关推荐

  1. Java 虚拟机学习笔记 | 类加载过程和对象的创建流程

    前言 创建对象是 Java 语言绕不开的话题,那么对象是如何创建出来的呢?我们今天就来聊一聊.对象创建第一步就是检查类是否加载,而类的加载又牵扯到类的加载过程.如果单说对象的创建而绕开类的加载过程,感 ...

  2. 深入理解JAVA虚拟机学习笔记(一)JVM内存模型

    摘要:   上周末搬家后,家里的宽带一直没弄好,跟电信客服反映了N遍了终于约了个师傅明天早上来迁移宽带,可以结束一个多星期没网的痛苦日子了.这段时间也是各种忙,都一个星期没更新博客了,再不写之前那种状 ...

  3. 深入理解Java虚拟机学习笔记-1.JVM内存模型

    JVM内存模型 1.内存模型结构图 名称 特征 作用 配置参数 异常 程序计数器 占用内存小,线程私有, 生命周期与线程相同 大致为字节码行号指示器 无 无 虚拟机栈 线程私有,生命周期与线程相同,使 ...

  4. Java虚拟机学习 - 体系结构 内存模型(转载)

    一:Java技术体系模块图 二:JVM内存区域模型 1.方法区 也称"永久代" ."非堆",  它用于存储虚拟机加载的类信息.常量.静态变量.是各个线程共享的内 ...

  5. java虚拟机学习笔记 【3】

    为什么80%的码农都做不了架构师?>>>    认识Java虚拟机的内部体系结构 Java虚拟机的内部体系结构也许很少有人去关心,因为对于Java程序员来说,一般只需要跟API打交道 ...

  6. Java虚拟机学习笔记(一)—Java虚拟机概述

    一:编程语言兼容底层系统的方式大概分为两种 1.通过编译器实现兼容 例如C.C++等编程语言,既能运行与Linux系统,也能运行与Windows系统:既能运行于x86平台,也能运行于AMD平台.这种能 ...

  7. Java 虚拟机学习笔记 | 运行时数据区总结

    前言 要想学习好 Java,Java虚拟(JVM)的学习是绕不开的.学习 Java虚拟(JVM)首先就要先了解的就是Java虚拟(JVM)运行时数据区. 在Java语言和虚拟机规范中对运行时数据区进行 ...

  8. 【深入理解Java虚拟机学习笔记】第三章 垃圾收集器与内存分配策略

    最近想好好复习一下java虚拟机,我想通过深读 [理解Java虚拟机 jvm 高级特性与最佳实践] (作者 周志明) 并且通过写一些博客总结来将该书读薄读透,这里文章内容仅仅是个人阅读后简短总结,加强 ...

  9. 【深入理解Java虚拟机学习笔记】第二章 Java 内存区域与内存溢出异常

    最近想好好复习一下java虚拟机,我想通过深读 [理解Java虚拟机 jvm 高级特性与最佳实践] (作者 周志明) 并且通过写一些博客总结来将该书读薄读透,这里文章内容仅仅是个人阅读后简短总结,加强 ...

最新文章

  1. 认清Hadoop和Spark的这几点区别,学习时才能事半功倍
  2. 开发ProxyServer的时候如何在一台PC上调试
  3. [笔记]C#基础入门(十四)——C#用流程图描述程序逻辑
  4. 软件测试实验4白盒测试,软件测试实验报告白盒测试
  5. Linux(Centos7)安装Docker
  6. VTK:几何对象之Cell3DDemonstration
  7. “Unamed VM”无法初始化 0x80070539
  8. java重载方法math_Java语言程序设计(十二)Math数学类,方法重载及变量作用域...
  9. Centos7 yum install chrome
  10. Jetson TK1 一:调整屏幕分辨率
  11. java利用poi生成/读取excel表格
  12. 英语----情态动词---半情态动词
  13. 使用 MySQL C API 访问 MySQL — 示例
  14. 【小程序合集】来一组适合你的表情包-表情包大全
  15. [3D分割 Benchmak] ScanNet: Richly-annotated 3D Reconstructions of Indoor Scenes
  16. 计算机电源风扇是吹风还是吸风,回答一些网友关于机箱内风道以及电源吸风还是抽风的问题...
  17. python如何用opencv把一个视频按每10秒一小段切割
  18. 图片+css实现波浪
  19. 微信浏览器iframe嵌套h5,h5页面不能调起微信支付问题处理
  20. Oracle EBS子库存转移,项目转移whole LPN

热门文章

  1. 计算机新教师培训自我评价,教师个人自我评价(精选多篇)
  2. 高等数学笔记:导函数与原函数关于函数性质的研究
  3. 如何找到脑电中眼电伪迹/EEG伪迹寻找/eeglab使用
  4. linux shell awk用法
  5. Altium Designer 22安装步骤
  6. 公网IP,内网IP,动态IP,静态IP的区别
  7. 淋巴瘤最新研究进展(2022年4月)
  8. android开发中遇到的异常及解决方法
  9. 第6章 放大器的频率特性
  10. Jmeter 压测工具