类加载器的种类 BootstrapClassLoader(启动类加载器)类加载器是java运行环境的一部分,主要负责动态加载Java类到Java虚拟机的内存空间中,学习类加载器需要掌握java的委派原理(双亲委派机制)。
Java语言是一种具有动态性的解释语言,类(CLASS) 只有被加载到 JVM 中后才能运行。当运行指定程序时,JVM会将编译生成的.class文件按照需求和一定的规则加载到内存中,组织成为一个完整的Java应用程序。
这个加载的过程是由类加载器来完成的,具体来说,就是由ClassLoader和它的子类来实现的。类加载器本身也是一个类,其实质是把类文件从硬盘读取到内存中。
称为启动类加载器是java类加载器中最顶层的类加载器,负责加载jdk的核心类库,如rt.jar、resources.jar、charsets.jar等。
通过下面的程序可以看出加载了哪些jar包和相关的class文件
从rt.jar中选择Object类,查看他的类加载器:
为什么Object的类加载器是null呢?
是因为Bootstrap ClassLoader是由C/C++编写的,它本身是虚拟机的一部分,所以它并不是一个JAVA类,也就是无法在java代码中获取它的引用。
ExtensionClassLoader(扩展类加载器)负责加载扩展jar包, jre/lib/ext/*.jar 和由java.ext.dirs系统属性指定的jar包,放在这个目录下的jar包对AppClaseLoder也是可见的(因为ExtClassLoader是AppClassLoader的父加载器,并且java类加载器采用的是委托机制)
AppClassLoader(应用类加载器)负责加载classPath指定内容或者java.class.path系统属性或者classPath *** 作系统属性所指定的JAR类包和类路径。
通过下面的代码可以查看AppClassLoader加载的路径:
通过下面的代码我们可以看到我们写类就是AppClassLoader加载的:
类的加载过程加载(Loding)
类的加载阶段是由类加载器根据这个类的全限定名来读取这个类的二进制字节流到JVM内部,然后将其转换为一个与目标类对应的Class对象实列,这个对象日后会作为方法区中该类的各种数据访问入口。
链接(linking)
链接阶段要做的是将JVM中的二进制字节流的类数据,经过验证、准备、解析三个阶段,整合到JVM运行时状态中;
验证(Verification)
格式验证:验证是否符合class文件规范;
语义验证:检查一个被标记为final的类型是否包含子类;检查一个类中的final方法视频被子类进行重写;确保父类和子类之间没有不兼容的一些方法声明(比如方法签名相同,但方法的返回值不同)
*** 作验证:在 *** 作数栈中的数据必须进行正确的 *** 作,对常量池中的各种符号引用执行验证(通常在解析阶段执行,检查是否通过富豪引用中描述的全限定名定位到指定类型上,以及类成员信息的访问修饰符是否允许访问等)
准备(Preparation)
为类中的所有静态变量分配内存空间,并为其设置一个默认值(因为对象还没有产生,实列的变量不可被 *** 作); 被final修饰的静态变量,会直接赋予原值;
解析(Resolution)
将类,方法,属性等符号引用解析为直接引用。常量池中的各种符号引用解析为指针,偏移量等内存地址的直接引用
初始化(Initializing)
调用类初始化代码 ,给静态成员变量赋初始值, 如果执行的是static代码块,那么在初始化阶段,JVM就会执行static代码块中定义的所有 *** 作。
双亲委派机制是类加载的一个自低向上,在自上向低的过程。
1.收到加载请求时,类加载器会首先判断是否加载过这个类,如果是就结束,否则会委托自己的父加载器进行加载,不断循环这个过程,直到将加载任务给到BootstrapClassLoader为止。 2.BootstrapClassLoader会判断能否加载完成这个类,如果可以加载就直接加载,否则就交给自己的子类加载器进行加载,不断重复这个步骤,直到最后一个类加载器进行加载,如果还是加载不到就抛出异常:ClassNotFoundException;
为什么要搞双亲委派机制主要是为了安全。
试想一下,如果不是双亲委派,有人写一个String类,把String获取到的用户名密码远程发送到对方的服务器,会产生什么样的后果。
减少资源的浪费。
为了不重复加载,减少资源的浪费。
严格意义上来说,我认为叫lazyInitializing(懒初始化)更加合适; JVM规范并没有规定何时加载; 但是严格规定了什么时候必须初始化;
类什么时候进行初始化:
测试public class LaxyLoading { public static void main(String[] args) throws ClassNotFoundException { P p; // a X x= new X(); // b System.out.println(P.i); // c System.out.println(P.j); // d } public static class P{ final static int i = 9; static int j = 10; static { System.out.println("加载 P 类"); } } public static class X extends P{ static { System.out.println("加载 X 类"); } } } 复制代码只保留代码a:
什么都不会打印,证明Jvm加载类是一个懒加载,没引用就不会进行初始化。 复制代码只保留代码b:
证明初始化子类时,父类会首先初始化 复制代码只保留代码c:
打印:9 //证明访问final修饰的的变量不会初始化 复制代码只保留代码d:
打印:(加载 P 类)(10) //证明获取静态变量类会初始化
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)