您好!欢迎来到爱源码

爱源码

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

第2章类装入器的详细说明 [互站网]

  • 时间:2022-11-01 00:09 编辑: 来源: 阅读:305
  • 扫一扫,手机访问
摘要:第2章类装入器的详细说明 [互站网]
微信搜索:全栈小刘,获取文章1的完整pdf版。内存结构概述。如果想手工写一个Java虚拟机,主要考虑哪些结构?类加载器执行引擎完整框图图片:图片2,类加载子系统,类加载器子系统的作用,类加载器子系统负责从文件系统或服务器网络加载类文件,类文件在文件开头有特定的文件标识。 ClassLoader只负责加载类文件,能不能运行由执行引擎决定。 加载的类信息存储在一个叫做方法区的内存空间中。 除了类信息,方法区还会存储运行时常量池信息,可能还包括字符串文字和数值常量(这个常量信息是常量池在类文件中的内存映射)image Class->:Java . lang . Class Class Class文件存在于本地硬盘上,可以了解一下设计者在纸上画的模板。最后,当模板被执行时,它将被加载到JVM中,根据这个文件实例化N个相同的实例。 类文件被加载到JVM中,称为DNA元数据模板,并放置在方法区域中。 在。类文件–> JVM –& gt;要成为最终的元数据模板,这个过程需要一个运输工具(类加载器)充当信使。 3.图3,类加载过程3.1。类装入过程概述。参见代码public class hello loader { public static void main(string[]args){ system . out . println("谢谢ClassLoader加载我...");System.out.println("你的恩情,我下辈子再报!");}}它的加载过程是什么样的?要执行main()方法(静态方法),需要先加载轴承类HelloLoader。如果加载成功,将执行链接、初始化等操作。如果HelloLoader类中的静态方法main加载失败,您将抛出一个异常图像。完整的流程图如下:*加载->:链接(验证->;准备好->:Parse)-& gt;初始化image3.2,加载阶段加载过程通过类的全限定名获取定义类的二进制字节流,将该字节流表示的静态存储结构转换为方法区的运行时数据结构,并在内存中生成表示该类的java.lang.Class对象。类文件作为方法区各类数据的访问点,直接从本地系统加载,通过网络获取。典型场景:从zip压缩包中读取Web Applet,成为以后jar和war格式的基本运行时计算和生成。应用最广泛的是:动态代理技术由其他文件生成。典型场景:JSP应用程序提取。专有数据库中的类文件。很少从加密文件中获得,典型的防止反编译类文件的保护措施3.3。链接阶段*链接分为三个子阶段:验证->;准备好->:解析image3.3.1,验证验证的目的是保证类文件的字节流中包含的信息符合当前虚拟机的要求,保证加载的类的正确性,不危及虚拟机本身的安全。主要包括四种验证:文件格式验证、元数据验证、字节码验证和符号引用验证。 比如用BinaryViewer查看字节码文件,文件开头是CAFE BABE。如果有非法字节码文件,会验证没有通过image3.3.2对类变量进行内存分配,设置类变量的默认初始值,即零值。这里不包括用final装饰的静态。由于默认值将在编译期间在final中赋值,因此它将在准备阶段显式初始化。注意:这里初始化不会赋给实例变量,类变量会赋给方法区,实例变量会和对象一起赋给Java堆。示例代码:变量A在准备阶段会赋一个初始值,但不是1,而是0,在初始化阶段会赋给1个公共类hello app { private static int A = 1;public static void main(String[]args){ system . out . println(a);}}3.3.3、Resolve(解析)将常量池中的符号引用转换为直接引用的过程。事实上,解析操作通常伴随着JVM在初始化之后执行符号引用。符号引用是描述被引用目标的一组符号。 引用的文字形式在java虚拟机规范的类文件格式中有明确的定义。 直接引用是间接定位到目标的指针、相对偏移量或句柄。解析动作主要针对类或接口、字段、类方法、接口方法、方法类型等。 常量池中相应的符号引用,如常量类信息、常量字段引用信息和常量方法引用信息。反编译类文件后,可以查看符号参考图3.4,初始化阶段。初始化阶段是执行类的构造函数方法 也就是说,当我们的代码包含静态变量时,就会有clinit方法* * < clinit & gt()& lt/clinit & gt;方法中的指令按照语句在源文件中出现的顺序执行* * < clinit & gt()& lt/clinit & gt;不同于类的构造函数 (关联:构造函数是 分别设置引导类加载器和用户定义的类加载器。从概念上讲,自己设置类加载器一般是指开发者在程序中设置类加载器,但Java虚拟机规范并没有这样定义。相反,所有从通用类类装入器派生的类装入器都被划分到它们自己的类装入器中。无论类装入器如何划分,程序中总是只有三种最常见的类装入器。如下图,这里四者之间是包含关系,不是上下级,也不是子类和父类的继承关系。 为什么image ExtClassLoader和AppClassLoader都属于自设置加载器的规范定义:所有从通用类加载器派生的类加载器都分为自设置类加载器ExtClassLoader继承树imageAppClassLoader继承树镜像代码:我们尝试获取boot类加加载器,得到的值为null,不代表bootloader不存在。由于bootloader的C/C++语言不对,我们无法两次获得系统类加载器的相同值:sun . misc . launcher $ app class loader @ 18 B4 AAC 2,这说明*系统类加载器是一个全局唯一的公共类加载器test { public static void main(string[]args){ class loader system class loader = class loader . getsystemclass loader();system . out . println(system class loader);class loader ext class loader = system class loader . get parent();system . out . println(ext class loader);class loader bootstrapClassLoader = ext class loader . get parent();system . out . println(bootstrapClassLoader);class loader class loader = classloadertest . class . get class loader();system . out . println(class loader);class loader class loader 1 = string . class . get class loader();system . out . println(class loader 1);}}4.2.虚拟机4.2.1附带的加载程序。引导类加载器(Bootstrap ClassLoader)这个类加载是用C/C++语言实现的。用于在JVM内部加载Java的核心库(JAVA_HOME/jre/lib/rt.jar,resources.jar或者sun.boot.class.path路径下的内容),用于提供JVM本身的类不继承自java.lang.ClassLoader没有父类加载器来加载扩展类和应用类加载器。而作为它们的父类加载器(当它们的父亲),出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等的4.2.2类。,扩展类加载器用Java语言编写。从ClassLoader类派生父类加载器由Sun实现。杂项启动器$ ext类加载器。要启动clas loader,需要从java.ext.dirs系统属性指定的目录中,或者从JDK安装目录的jre/lib/ext子目录(扩展目录)中加载类库。 如果客户创建的JAR放在这个目录下,会被扩展类加载器4.2.3自动加载,系统类加载器(app class loader)用Java语言编写。从ClassLoader类派生父类加载器由Sun实现。MISC. Launcher AppClassloader,这是一个扩展类加载器。它负责在环境变量classpath或系统属性java.class.path指定的路径下加载类库,这个类加载器是程序中默认的类加载器。一般来说,Java应用程序的类都是由它加载的。这个类的加载器代码可以通过class loader . getsystemclassloader()方法获得。示例代码为public class loader test1 { public static void main(string[]args){ system . out . println(" * * * * * * * * * *启动类加载器* * * * * * * * * * *);URL[]urLs = sun . misc . launcher . getbootstrapclasspath()。getURLs();for (URL元素:urLs){ system . out . println(element . toexternalform());} class loader class loader = provider . class . get class loader();system . out . println(class loader);System.out.println ("* * * * * * * * *扩展类加载器* * * * * * * * * * ");string ext dirs = system . getproperty(" Java . ext . dirs ");for(字符串路径:ext dirs . split(";")){ System.out.println(路径);} class loader class loader 1 = curvedb . class . get class loader();system . out . println(class loader 1);} } system . out . println(class loader);输出null,再次确认我们无法获取启动类加载器* * * * * * * * * * & # x542F& amp# x52A8& amp# x7C7B& amp# x52A0& amp# x8F7D& amp# x5668* * * * * * * * * * * * * *文件:/C:/Program % 20 files/Java/JDK 1 . 8 . 0 _ 144/JRE/lib/resources . jar文件:/C:/Program % 20 files/Java/JDK 1 . 8 . 0 _ 144/JRE/lib/rt . jar文件:/C:/Program % 20 files/Java/JDK 1 . 8 . 0 _ 144/JRE/lib/sunrsa sign . jar文件:/C:/Program % 20 files/Java/JDK 1 . 8 . 0# x6269& amp# x5C55& amp# x7C7B& amp# x52A0& amp# x8F7D& amp# x5668* * * * * * * * * * c:\ program files \ Java \ JDK 1 . 8 . 0 _ 144 \ JRE \ lib \ extc:\ windows \ sun \ Java \ lib \ ext sun . misc . launcher $ ext class loader @ 77在Java的日常应用开发中,类的加载几乎都是由上述三个类加载器来执行的。必要时,我们也可以自己设置类加载器,自定义类加载方式。 那你为什么需要设置自己的类装入器呢?隔离加载类,修改类加载方式,扩展加载源,防止源代码泄露。如何自己设置类加载器?开发人员可以通过继承通用类java.lang.ClassLoader类来实现自己的类加载器,以满足少量特殊需求。在JDK1.2之前,在设置自己的类加载器时,总是继承class loader类,重写loadClass()方法,从而实现自己的set类加载类。但是,在JDK1.2之后,不再建议客户重写loadClass()方法。相反,建议他们在findclass()方法中编写自己的set类加载逻辑。在编写自己的set类加载器时,如果没有太复杂的需求,可以直接继承URIClassLoader类,这样就可以避免编写findclass()方法和获取字节码流的方式,让自己的set类加载器的编写更加简洁。 代码公共类自定义类加载器扩展类加载器{@ override protected class <?& gtfindClass(String name)抛出ClassNotFoundException { try { byte[]result = getClassFromCustomPath(name);if(result = = null){ throw new FileNotFoundException();} else { return defineClass(name,result,0,result . length);} } catch(file not found exception e){ e . print stack trace();}抛出新的ClassNotFoundException(name);} private byte[]getClassFromCustomPath(String name){ return null;} public static void main(String[]args){ custom class loader custom class loader = new custom class loader();尝试{ Class & lt?& gtclazz = Class.forName("One ",true,custom class loader);object obj = clazz . new instance();system . out . println(obj . getclass()。get class loader());} catch(Exception e){ e . printstacktrace();} }}4.4、关于ClassLoaderClassLoader类详情ClassLoader类,它是一个通用类,所有后续的类加载器都继承自ClassLoader(不包括启动类加载器)imagesun.misc.Launcher,它是一个java虚拟机的门户。应用图像以获得类加载器。Access: image代码示例:public class loader test2 { public static void main(String[]args){ try { class loader class loader = class . forname(" Java . lang . String ")。get class loader();system . out . println(class loader);class loader class loader 1 = thread . current thread()。getContextClassLoader();system . out . println(class loader 1);class loader class loader 2 = class loader . getsystemclass loader();system . out . println(class loader 2);} catch(ClassNotFoundException e){ e . printstacktrace();} } } nullsun . misc . Launcher $ app class loader @ 18 B4 AAC 2 sun . misc . Launcher $ app class loader @ 18 B4 AAC 25、父母委托机制5.1、父母委托机制原理Java虚拟机是按需加载类文件的,也就是说当它需要使用这个类的时候,它的类文件会被加载到内存中生成类对象。 而且在加载一个类的类文件时,Java虚拟机采用的是父委托模式,即请求交给父类,这是一种任务委托模式。如果一个类加载器收到一个类加载请求,它不会先加载,而是把请求委托给父类的加载器执行;如果父类装入器仍有其父类装入器,则进一步向上递归委托,请求最终到达顶层启动类装入器;如果父类装入器能够完成类装入任务,它将成功返回。如果父类装入器不能完成这个装入任务,子类装入器将尝试自己装入。这是家长委托模式。 父类装入器逐层分配任务。如果子类加载器可以加载,它将加载这个类。如果将加载任务分配给系统类加载器,则不会加载该类,会抛出异常image5.2 .父委托机制代码示例示例代码示例1:代码:我们会自己构建一个java.lang.String类,编写静态代码块包java.langpublic String { static { system . out . println("我是自己设置的string类的静态代码块");}}在另一个程序中加载String类,看看加载的String类是JDK自带的String类,还是String类public class String test { public static void main(String[]args){ Java . lang . String str = new Java . lang . String();System.out.println("你好,at guigu . com ");string test test = new string test();system . out . println(test . getclass()。get class loader());}}程序并没有输出我们静态代码块中的内容,所以可以看出还是加载了JDK自己的String类图片例2:代码:我们自己的String类中的整个main()方法包java.langpublic String { static { system . out . println("我是自己设置的string类的静态代码块");} public static void main(String[]args){ system . out . println(" hello,String ");}}}}因为父委托机制找到了JDK附带的字符串类,所以在那个字符串类中没有main()方法映像。例3:代码:将java.lang打包在整个ShkStart类下;public class shk start { public static void main(String[]args){ system . out . println(" hello!");}}由于保护机制的原因,我们不允许在java.lang包下设置类映像。例4:我们在加载jdbc.jar进行数据库连接的时候,首先要知道的是jdbc.jar是基于SPI接口实现的,所以加载的时候父母会委托,最后从根加载器加载SPI核心类,然后加载SPI接口类,然后进行反向委托,通过线程上下文类加载器加载实现类jdbc.jar。 图5.3、父母委托机制的优点父母委托机制的优点通过上面的例子我们可以知道,父母机制可以避免重复加载类来保护程序安全,防止核心API被任意篡改自己的设置类:java.lang.String没有使用自己的设置类:java.lang.ShkStart(错误:防止创建以java.lang开头的类)6 .沙盒安全机制设置自己的字符串类时:加载自己的设置字符串类时,会率先使用引导类加载器加载。而boot class loader在加载过程中会先加载jdk自己的文件(rt.jar包中的java.lang.String.class),并且错误信息说没有main方法,只是因为加载了rt.jar包中的String类。 这样可以保证java核心源代码的保护,也就是沙盒安全机制。 7.如何判断两个类对象是否相同?在JVM中有两个必要条件来表示两个类对象是否可以是同一个类:类的完整类名必须相同,包括包名。这个类的ClassLoader(引用ClassLoader实例对象)必须相同。换句话说,在JVM中,即使两个类对象(class object)来源于同一个类文件,由同一个虚拟机加载,但是,只要加载它们的ClassLoader实例对象不同,这两个类对象也是对classloader的不平等引用。JVM必须知道一个类型是由引导装入器还是客户机类装入器装入的。如果一个类型是由客户机类装入器装入的,那么JVM将把这个类装入器的引用作为类型信息的一部分保存在方法区域中。当解析从一种类型到另一种类型的引用时,JVM需要确保这两种类型的类装入器是相同的。主动使用和被动使用Java程序以两种方式使用类:主动使用和被动使用。 主动使用可以分为七种情况:创建类的实例来访问类或接口的静态变量,或者调用类的静态方法反射(例如:class . forname(" com . atguigu . test ")赋给静态变量。)初始化一个类的子类。JDK7提供的动态语言支持,Java虚拟机启动时标记为启动类:java.lang.invoke.MethodHandle实例REF_getStatic、REF putStatic的解析结果,以及REF_invokeStatic handle对应的类没有初始化,那么初始化除了以上七种情况,Java类的其他使用方式都被视为类的被动使用,不会导致类初始化,即初始化阶段不会被执行(clinit()方法和init()方法不会被调用)。


  • 全部评论(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
手机版
手机版
扫一扫进手机版
返回顶部