jdk类加载机制,java类加载机制使用什么模型
很长一段时间,我对Java的类加载机制非常抵触,因为我觉得太难理解了。但是为了成为一名优秀的Java工程师,我决定做一些研究。
如何解决写爬虫IP受阻的问题?立即使用。
01、字节码
在讲Java类加载机制之前,我们需要了解Java字节码,因为它与类加载机制密切相关。
计算机只知道0和1,所以任何语言编写的程序都需要编译成机器代码才能被计算机理解然后执行,Java也不例外。
Java诞生的时候喊了一句非常牛逼的口号:“一次编写,随处运行”。为了实现这个目标,Sun公司发布了多个Java虚拟机(JVM)——,它们可以运行在不同的平台(Windows、Linux)上,负责加载和执行Java编译的字节码。
让我们借助一个简单的代码来看看Java字节码是什么样子的。
源代码如下:
包com . cmower . Java _ demo;
公共类测试{
公共静态void main(String[] args) {
System.out.println(版权声明);
}
}代码编译通过后,通过xxd Test.class命令检查这个字节码文件。
xxd测试类
00000000:咖啡宝贝0000 0034 0022 0700 0201 0019.4. .
00000010:636 f 6d2f 636d 6f 77 6572 2f6a 6176 615 f com/cmower/Java _
00000020:6465 6d6f 2f 54 6573 7407 0004 0100 106 a演示/测试.j
00000030:6176 612 f 6c 61 6e 67 2f 4f 626 a 6563 7401 ava/lang/Object。
00000040:0006 3c 69 6e 69 743 e 0100 0328 2956 0100.初始化.()五.
00000050:0443 6f 64 650 a 0003 0009 0c 00 0500 0601。密码..
0000060:000 f 4c 69 6e 65 4e 75 6d 62 6572 5461 626 c.行号TABL感觉有点不知所措吧?
没错。
这个字节码里的Cafe babe叫做“幻数”,是JVM认可的标志。类文件。文件格式的定制者可以自由选择幻数(只要不用)。例如,的幻数。png文件是8950 4e47。
至于其他内容,你可以选择忘记。
02.类别加载过程
了解了Java字节码之后,再来说说Java的类加载过程。
Java类加载过程可以分为五个阶段:加载、验证、准备、解析和初始化。通常,这五个阶段依次发生,但是在动态绑定的情况下,解析阶段发生在初始化阶段之后。
1)装载(Loading)
JVM在这个阶段的主要目的是将来自不同数据源(可能是类文件、jar包,甚至是网络)的字节码转换成二进制字节流并加载到内存中,生成一个表示这个类的java.lang.Class对象。
2)验证(Verification)
在这个阶段,JVM将检查二进制字节流,只有那些符合JVM字节码规范的字节流才能被JVM正确执行。这个阶段是保证JVM安全性的重要屏障。以下是一些主要检查。
确保二进制字节流格式符合预期(例如,是否以cafe bene开头)。
是否所有方法都符合访问控制关键字的限制。
方法调用的参数的数量和类型是否正确。
确保变量在使用前被正确初始化。
检查变量是否被赋予了适当类型的值。
3)准备(准备)
在这个阶段,JVM会给类变量(也叫静态变量,用static关键字修改)分配内存并初始化(对应数据类型的默认初始值,如0,0L,null,false等。).
也就是说,如果有这样一个代码:
公共字符串陈墨=沉默;
公共静态字符串wanger= wanger ;
Public static final String cmower=沉默王二;陈墨不会被分配内存,但王二会;但王二的初始值不是“王二”而是null。
需要注意的是,static final修改的变量称为常量,与类变量不同。常量一旦赋值就不会改变,所以准备阶段cmower的值是“沉默的国王II”而不是null。
4)分辨率(Resolution)
在这个阶段,常量池中的符号引用被转换成直接引用。
什么?符号引用,直接引用?
符号是指一组符号(任何形式的文字量,只要在使用时可以明确定位目标)来描述所引用的目标。
在编译时,Java类不知道被引用类的实际地址,所以只能用符号引用来代替。例如,com。王二类是指通讯器。陈墨级。编译时,Wanger类不知道陈墨类的实际内存地址,所以只能使用符号com.chenmo
引用直接解析符号引用以找到引用的实际内存地址。
5)初始化(Initialization)
这个阶段是类加载过程的最后一步。在准备阶段,类变量已经被赋予了默认的初始值,而在初始化阶段,类变量将被赋予代码所期望的值。换句话说,初始化阶段是执行类构造函数方法的过程。
哦,不对,上面那段很抽象,很难理解吧?我给你举个例子。
String cmower=new String(沉默王二);
上面的代码使用new关键字实例化一个string对象,所以这个时候就会调用String类的构造函数实例化cmower。
03.类装入器
说完了类装入过程,就不得不说一下类装入器。
一般来说,Java程序员不需要和类似的加载器直接交互。JVM的默认行为对于大多数情况来说已经足够了。但是,如果你需要和类加载器交互,而你又不太了解类加载器的机制,你就要花很多时间去调试。
异常,如ClassNotFoundException和NoClassDefFoundError。
对于任何一个类,它的类加载器和类本身都需要确定它在JVM中的唯一性。也就是说,如果两个类的加载器不同,即使两个类源自同一个字节码文件,那么两个类也一定不相等(比如两个类的类对象不相等)。
从程序员的角度来看,Java类装入器可以分为三种类型。
1)启动Bootstrap Class-Loader,加载jre/lib包下的jar文件,比如common rt.jar
2)扩展类加载器(Extension或Ext Class-Loader)来加载jre/lib/ext包下的jar文件。
3)应用程序或App类加载器根据程序的类路径加载Java类。
来,我们通过一个简单的代码来了解一下。
公共类测试{
公共静态void main(String[] args) {
class loader loader=test . class . get class loader();
while (loader!=null) {
system . out . println(loader . tostring());
loader=loader . get parent();
}
}
}每个Java类都维护一个对定义它的类加载器的引用,可以通过类名获得。class . getclass loader();那么可以通过loader.getParent()获取类加载器的上层类加载器。
这段代码的输出如下:
sun . misc . launcher $ app class loader @ 73d 16 e 93
sun . misc . launcher $ ext class loader @ 15db 9742的第一行输出Test的类加载器,即应用程序类加载器,它是sun . misc . launcher $ app class loader类的一个实例;第二行输出是扩展类加载器,它是sun.misc.launcher $ ext类加载器类的一个实例。启动类加载器怎么样?
按理说扩展类加载器的上层类加载器是启动类加载器,但是在我的JDK版本中,扩展类加载器的getParent()返回null。所以没有输出。
04、双亲委派模型
如果以上三个类加载器不能满足要求,程序员还可以自定义类加载器(继承java.lang.ClassLoader类)。它们之间的层级关系如下图所示。
这种层次关系被称为父委托模型:如果一个类加载器收到加载一个类的请求,它会先把请求委托给上层加载器,上层加载器再把上层加载器委托给顶层类加载器;如果上层加载器不能完成加载类,当前的类加载器将尝试自己加载类。
PS:父母任命的模式突然让我想起了朱元璋同志。他当了皇帝之后,连宰相都不要了。他什么都亲力亲为,只有自己没精力没时间做的事才交给大臣们。
使用父委托模型有一个明显的优势,就是Java类与其类加载器有一个带优先级的层次关系,这对于保证Java程序的稳定运行非常重要。
如上所述,如果两个类的加载器不同,即使这两个类来自同一个字节码文件,那么这两个类一定是不相等的。——父母委托模型可以保证同一个类最终由特定的类装入器装入。这就是Java的类加载机制的细节。更多请关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。