您好!欢迎来到爱源码

爱源码

热门搜索: 抖音快手短视频下载   

了解更多关于JVM类加载机制的信息。 《源码交易》

  • 时间:2022-11-01 00:09 编辑: 来源: 阅读:304
  • 扫一扫,手机访问
摘要:了解更多关于JVM类加载机制的信息。 《源码交易》
类的生命周期分为七个阶段:JVM类加载机制。类加载过程由加载、验证、准备、分析和初始化五个部分组成,其中验证、准备和分析也称为链接。 这些过程并不是严格线性的,它们会在中间执行。 例如,在加载完成之前,连接过程可能已经开始(例如,文件格式验证);例如,解析可以发生在初始化之前或之后,等等。 现在对这五个阶段进行详细分析。 1.将编译后的类文件加载到内存中,创建一个java.lang.Class对象。 在这个阶段有三件事要完成:使用类的完全限定名来获取类的二进制字节流。 转换二进制字节流的静态结构,并将其存储为方法区域的运行时结构。 在内存中生成一个java.lang.Class对象作为访问门户。 经常使用Java反射会让你对java.lang.Class对象非常熟悉,因为你可以从这个对象中获得很多种信息,比如字段、方法等。 不同的类装入器可以从不同的源装入类文件,例如,可以直接装入本地类文件。 例如,在。类路径路径下的类文件 网络加载 装入压缩包、zip、jar或war。 比如第三方类库打包压缩在jar里,部署到tomcat的类文件打包在war里。 运行时创建 Java提供了少量的工具类来实现,比如java.lang.reflect包中的代理工具,可以直接在内存中创建动态代理对象。 由其他文件生成,如JSP文件。 JVM-类加载机制-加载我们也可以设置自己的类加载器从其他通道加载类文件。 内存加载后,生成的java.lang.Class对象非常特殊,因为它存储在方法区而不是堆中。 2.验证验证是连接的第一步。 检查类文件的格式,确保它符合JVM规范,避免虚拟机的安全问题。 由于上面提到的类文件不一定是由javac编译生成的,因此有多种方法可以创建它。对于编译时数组越界、对象类型转换错误等问题,需要重新检查类加载过程。 然而,验证仍然相当耗费性能。如果类文件已经被重复验证过,可以使用-Xverify:none来缩短类的加载时间。 有四种验证:文件格式验证、元数据验证、字节码验证和符号引用验证。 2.1.文件格式验证。验证文件格式,以确保它符合类文件规范,并且可以由虚拟机的当前版本解决。 例如,检查幻数是否可以是类文件(0xCAFEBABY) 检查主版本和次版本是否正确。 检查常量池的类型和索引是否正确。 ......在这个阶段之后,字节流将被存储在方法区。 因此,文件验证发生在加载阶段结束之前。 文件格式验证后,后续验证基于方法区域。 2.2.元数据验证元数据验证,字节码语义分析,看是否能符合语法规范。 比如可以有父类吗? 除了java.lang.Object,所有类都必须有父类。 可以继承不允许被继承的类吗 例如,final修饰的类不允许被继承。 如果不是通用类,是否可以实现父类或者接口要求的方法? 类的字段和方法会和父类冲突吗? 例如,父类的最后一个字段被覆盖。 ......如果这些语法规范是编译后的类文件,那么在编译过程中也会检查它们。 但是由于类文件的来源多种多样,不能保证遵循语法规范,这里再做一个验证。 2.3.字节码验证 分析字节码数据流和控制流,确保语义正确。 这是所有验证中最复杂的阶段。 例如:正确的操作说明 操作局部变量表和操作数栈的数据,指令类型要正确。 正确跳转指令 跳到正确的字节码。 的正确类型转换 例如,子类可以分配给父类,但是父类不允许分配子类。 ......这里有一个验证的例子。 在执行字节码时,对局部变量表和操作数栈的数据进行操作时,需要使用正确的指令类型。 比如局部变量表的index 1是int类型,需要iload_1字节码来加载,而不是lload_1,fload_1,dload_1。 在元数据验证阶段,可以强制绑定,杜绝误用。 public int add(int,int);描述符:(ii)I flags:ACC _ public code:stack = 2,locales = 3,args _ size = 3 0: iload _ 1 #由于局部变量类型为int,所以使用iload加载1: iload _ 2 2: iadd3: ireturn行号表:line32: 0局部变量表:起始长度槽名签名0 4 0 this lob lee/demo/JVM/stack/simple object;0 4 1 a I #局部变量类型为int 0 4 2 b I #局部变量类型为int。经过复杂的字节码验证,仍然不能保证安全运行。 这属于著名的“停机问题” 复杂的验证也消耗了很多性能,热点虚拟机做了很多优化。 2.4.符号引用验证 当符号引用转换为直接引用时会出现这种情况,这属于解析阶段的验证,用于确认引用肯定会被访问。 比如符号引用的全限定名能找到对应的类吗? 符号引用的字段描述符或方法描述符能在类中找到对应的字段和方法吗? 被引用的类、字段和方法的范围(公共的、受保护的、私有的、默认的)能被当前的类访问吗? ......符号引用验证也表明这些阶段不是线性的,而是交替执行的。 如果无法验证符号引用,则会抛出相关异常,如java.lang.IllegalAccessError、java.lang.NoSuchFieldError、java.lang.NoSuchMethodError等。 3.准备为类变量分配内存,设置初始值。 分布的初始值是什么?每个数据的默认零值,包括0,0L,null,false等。 比如下面的类变量A:公共类简单对象{公共静态字符串A = " hello world公共静态字符串b = 2;}在准备阶段,A的初始值是null而不是“hello world”字符串,B的初始值是0而不是2。 a和b的真正赋值发生在初始化阶段,并放在类构造函数中 以上是类变量的求解过程,不是常量。 如果是基本数据类型的字符串或常量类型(int、long、float等。),具体数据会在准备阶段直接分配。 或者SimpleObject对象,我们用final关键字把A和B修饰成类常量:public class simple object { public static final string A = " hello world ";公共静态最终字符串b = 2;}准备阶段会直接从运行时常量池中取出“hello world”字符串给A赋值,从常量池中取出2对B(此时常量池中会加2)。 注意,准备阶段只解决类变量,不解决实例变量。 在类被实例化后,实例的变量与实例对象一起被分配到堆中。 4.解析的过程就是将符号指称转化为直接指称的过程。 符号主要包括:类和接口的全限定名。 字段名和描述符 方法名称和描述符 对应的类文件中常量的类型有CONSTANT_Class_info、CONSTANT_Fieldref_info和CONSTANT_Methodref_info。 编译后会在类文件的静态常量池中,类加载后会进入运行时常量池。 之所以需要符号引用,是因为javac将代码编译成类文件时,并不知道这些类、字段和方法在内存中的位置,所以需要一个符号来代替。 比如我们有一个ObjectA类,它将ObjectB作为成员变量引用:public class ObjectA { private ObjectB;public void setB(ObjectB b){ this . b = b;} public ObjectB getB(){ return b;}}用javap查看这个类的符号引用:JVM-类加载机制-符号引用直接引用主要有:目标指针,比如方法区的类对象、类变量、类方法的指针。 相对偏移量,如堆中实例变量的相对偏移量。 可以间接定位到目标的句柄。 可以直接引用内存中已经存在的对应目标。 直接引用与虚拟机的内存布局有关,不同虚拟机中的直接引用是不一样的。 这些都是解析前的字符串,存放在方法区的运行时常量池中(HotSpot 1.8方法区的实现做了调整,符号引用放在本地内存组成的元空间中)。字节码解释器在执行字节码时不能直接使用符号引用,所以需要将其转换成直接引用。要么是内存中的这个解析过程不确定在初始化之前执行,要么是可能延迟到需要使用符号引用,在堆栈帧中进行动态链接。 初始化前对JVM-虚拟机栈-栈帧-动态链接的分析称为静态绑定,栈帧内的分析称为动态绑定。 解析后,符号引用对应的直接引用将被记录在运行时常量池中。如果重复解析,将直接返回对应的直接引用。 5.在初始化被加载和连接(检查、准备和解析)之后,类被加载到内存中,初始内存被分配,初始化开始。 在加载过程中,我们可以设置自己的类加载器(例如,ClassLoader)来接管加载。连接完全由虚拟机自动解决,字节码只有初始化后才正式执行。 初始化就是执行类构造函数 & ltclinit & gt()是静态{}语句块的类变量赋值和代码的组合,不是必须的。如果没有静态变量赋值过程,就不会生成 在 类SimpleObject准备如下:公共类simple object { public static string a = " hello world ";public static int b = 100public static ObjectC c = new ObjectC();公共静态类d = ObjectD.classpublic static int e = ObjectE.e公共静态int f = objectf . getf();public static int h = 10公共静态字符串i = ObjectI。我;public static int k = ObjectJ.k静态{ ObjectH.h = h}}使用javap查看SimpleObject的类构造函数;:静态{ };描述符:()V flags: ACC_STATIC Code: stack=2,locals=0,args _ size = 0 0:LDC # 2//String hello world 2:put STATIC # 3//Field a:Ljava/lang/String;5:bi push 100 7:put static # 4//Field b:I 10:new # 5//class oblee/demo/JVM/pre/ObjectC 13:dup 14:invoke special # 6//Method oblee/demo/JVM/pre/ObjectC。“& ltinit >;":()V 17:put static # 7//Field c:lob lee/demo/JVM/pre/ObjectC;20:LDC # 8//Class oblee/demo/JVM/pre/ObjectD 22:put static # 9//Field d:Ljava/lang/Class;25:get static # 10//Field oblee/demo/JVM/pre/objecte . e:I 28:put static # 11//Field e:I 31:invoke static # 12//Method oblee/demo/JVM/pre/objectf . getf:()I 34:put static # 13//Field f:I 37:bi push 10 39:put static # 14//Field h:I 42:LDC # 16//String objectI 44:put static # 17//Field I:LJ47:get static # 18//Field oblee/demo/JVM/pre/objectj . k:I 50:put static # 19//Field k:I 53:get static # 14//Field h:I 56:put static # 20//Field oblee/demo/JVM/pre/Object h . h .:I 59:return如果一个静态变量是string类型,则作为文字量添加到常量池中,初始化时从常量池中取出赋值。 比如上面的“hello world”直接从常量池中取出,赋给a。 如果静态变量是基本数据类型,请使用指令直接加载它。 就像上面的bipush命令。 如果一个静态变量是一个类实例的引用类型,但是该类还没有被加载,就会再次触发这个类的类加载过程。 类加载后不会立即执行初始化,并且存在某些触发条件。 JVM规范没有指定何时开始类加载过程,但是对于类初始化有严格的规则。有四种字节码指令:new、getstatic、putstatic和invokestatic。如果没有初始化,将首先触发初始化。 常见的情况有:用new关键字实例化一个对象 面对静态变量C赋值,需要实例化类ObjectC,所以会触发ObjectC的初始化。 读取该类的静态变量 静态变量e的上述赋值,getstatic #10,触发了ObjectE的初始化。 设置类的静态变量 设置上面ObjectH的静态变量h,putstatic #20,触发ObjectH的初始化。 调用该类的静态方法。 上面调用了ObjectF的静态方法getF(),invokestatic #12,触发了ObjectF的初始化。 比如以下情况,不会进行类初始化:父类的静态字段被子类引用,只触发父类的初始化,不触发子类的初始化。 使用对象数组不会触发该类的初始化。 常量存储在常量池中,不直接引用常量的类不会触发定义常量的类的初始化。 通过类名获取类对象不会触发初始化。 例如,使用上面的ObjectD.class不会触发ObjectD类的初始化。 使用Class.forName()加载该类。如果参数initialize为false,将不会触发初始化。 通过类加载器的默认loadClass方法加载类不会触发初始化。


  • 全部评论(0)
资讯详情页最新发布上方横幅
最新发布的资讯信息
【技术支持|常见问题】1556原创ng8文章搜索页面不齐(2024-05-01 14:43)
【技术支持|常见问题】1502企业站群-多域名跳转-多模板切换(2024-04-09 12:19)
【技术支持|常见问题】1126完美滑屏版视频只能显示10个(2024-03-29 13:37)
【技术支持|常见问题】响应式自适应代码(2024-03-24 14:23)
【技术支持|常见问题】1126完美滑屏版百度未授权使用地图api怎么办(2024-03-15 07:21)
【技术支持|常见问题】如何集成阿里通信短信接口(2024-02-19 21:48)
【技术支持|常见问题】算命网微信支付宝产品名称年份在哪修改?风水姻缘合婚配对_公司起名占卜八字算命算财运查吉凶源码(2024-01-07 12:27)
【域名/主机/服务器|】帝国CMS安装(2023-08-20 11:31)
【技术支持|常见问题】通过HTTPs测试Mozilla DNS {免费源码}(2022-11-04 10:37)
【技术支持|常见问题】别告诉我你没看过邰方这两则有思想的创意广告! (2022-11-04 10:37)

联系我们
Q Q:375457086
Q Q:526665408
电话:0755-84666665
微信:15999668636
联系客服
企业客服1 企业客服2 联系客服
86-755-84666665
手机版
手机版
扫一扫进手机版
返回顶部