从源码理解类加载

      我们都知道,我们得java程序得运行,实际是根据面向对象编程得原理,为一个个类创建对象,对象们协同工作,完成了程序得运行!

      但是这些类,说到底是一个个得文件,二进制的class,如何变成为jvm所用的对象呢?,我们称之为类加载!看classloader偶有心得,随笔以记!

 

1  类加载原理(双亲委派机制)

     说到双亲委派机制,那就祭出一张十分眼熟得图!

 

 

        这是一张很简单易懂得图!就是类加载是有父子关系得,当接受到类加载请求时,都是先去找自己得“爸爸”类加载去完成加载,找到则返回,找不到则跑错!可参考下图源码理解:

 

 

        上图得这段代码就是classload种得loadClass方法实现! 上图可见,根据一个name加载类时,第一步会根据name得到一个锁,保证类加载得并发安全!第二步就是判断父类加载器是否为空,如果不为空,这调用父类加载得loadClass方法加载,如果为空,则第三步使用根加载器BootStrap根据名称加载,如果以上步骤完成后,依然没加载到类,在第四步调用findClass(name),实际是抛出一个 ClassNotFoundException(name)的异常!

       类加载器主要有四种,根加载,扩展加载,应用加载,自定义加载,其父子顺序主要可见 sun.misc.Lancher中代码:

 

        在Lancher的构造函数中,首先创建了ExtClassLoader 扩展加载,没有传递parent加载器,则为根加载器BootStrap加载器为parent,而后又将ExtClassLoader 做为parent属性,用以创建了AppClassLoader应用加载!

       在Lancher中有一个根目录属性 bootClassPath,通过 System.getProperty(“sun.boot.class.path”); 如果将其打印,可以发现是jdk下的lib下的一些jar包和classee目录!在Lancher中的静态内部类ExtClassLoader 实现了扩展加载,在调用getExtClassLoader时会调用createExtClassLoader方法给单例模式的ExtClassLoader  instrance创建,在createExtClassLoader方法中可见调用了 getExtDirs()方法,实际是通过System.getProperty(“java.ext.dirs”)获取扩展加载的类,打印可见该目录是jre\lib\ext目录。

      在Lancher的静态内部类 AppClassLoader中实现了应用加载,在调用 getAppClassLoader方法获取应用加载器的代码中可见调用了System.getProperty(“java.class.path”) 获取了开发人员指定的resource路径或默认路径。

      在这个完整的模式下,加载的类可以被它的类加载器缓存,没必要重复加载,同时,如果定义了同一个名称的两个类,也只会加载一个,保证了安全。

      通过阅读ClassLoader的代码可见,自定义类加载,实现ClassLoader即可,通过调用loadclass实现类加载! 如果想打破双亲委派加载模型,在自己重写的loadClass方法中实现即可。

     那么把一个class文件从jar包中读取生成class对象,又可见以下图:

 

       加载: 通过类的全限定名获取二进制字节流,把这个字节流代表的静态存储结构转化为方法区的运行时数据接口,在内存中创建一个Class对象作为方法区内这个类的访问入口。

       验证:1 文件格式验证:

                  a  class文件以0xCAFEBABE(咖啡baby)开头

                  b   版本号是否在当前虚拟机可接受范围

                  c   常量池中常量是否有不支持的类型

                  ……..

                  2 元数据验证 : 主要是验证语义,语法是否复合java语言规范,比如 是否有父类,父类是否是被final修饰过,如果不是抽象类,是否已完全实现抽象方法等

                  3 字节码验证 : 主要是验证语义预发是否合乎逻辑,注意,是逻辑,不同2中的规范。比如long类型数据被以int加载到本地

                  4 符合引用验证,验证类中用到的引用是否自己有通过全限定名访问的权限

       准备:为类中定义的静态变量分配内存

       解析:解析内部的字段和方法,把符号引用替换为直接引用

       初始化 :静态变量获取到了指定句柄,既指定值,执行静态代码。

 

       以上为类加载的过程,那合适会出发类加载呢?有以下条件触发:

     1  遇到new  getstatic putstatic  invokestatic四个指令时,如果没有初始化,则需要触发初始化。这4个指令通常是new 实例化对象,读取或设置static属性,调用静态方法是。

     2  对一个类使用反射包下方法调用时

     3 初始化一个类,但是发现起父类没有初始化时

     4 虚拟机启动时,执行一个主类

 

版权声明:本文为huqk-nn原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/huqk-nn/p/15003344.html