《深入分析JavaWeb技术内幕》之 6- 深入分析ClassLoader工作机制

 深入分析ClassLoader工作机制

Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取Java字节代码,并转换成 java.lang.Class类的一个实例。每个这样的实例用来表示一个Java 类。通过此实例的 newInstance()方法就可以创建出该类的一个对象。

Java 中的类加载器大致可以分成两类:

一类是系统提供的:

  • 引导类加载器(bootstrapclass loader):它用来加载 Java 的核心库,是用原生代码而不是java来实现的,并不继承自java.lang.ClassLoader,除此之外基本上所有的类加载器都是java.lang.ClassLoader类的一个实例。
  • 扩展类加载器(extensionsclass loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录(一般为%JRE_HOME%/lib/ext)。该类加载器在此目录里面查找并加载 Java 类。
  • 系统类加载器(systemclass loader或 App class loader):它根据当前Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader() 来获取它。

另外一类则是由 Java 应用开发人员编写的:

  • 开发人员可以通过继承java.lang.ClassLoader 类的方式实现自己的类加载器,以满足一些特殊的需求

 classLoader是类加载器,负责将Class加载到JVM中,还有一个作用是审查每个类由谁加载,它是一种父优先的等级加载机制。

    还有一个任务是,将class字节码重新解析成JVM统一要求的对象格式。

   

     加载类过程:

  假如loader2的parent为loader1,loader1的parent为system class loader。假设loader2被要求装载类MyClass,在parent delegation模型下,loader2首先请求loader1代为装载,loader1再请求系统类装载器去装载MyClass。若系统装载器能成功装载,则将MyClass所对应的Class对象的reference返回给loader1,loader1再将reference返回给loader2,从而成功将类MyClass装载进虚拟机。若系统类装载器不能装载MyClass,loader1会尝试装载MyClass,若loader1也不能成功装载,loader2会尝试装载。若所有的parent及loader2本身都不能装载,则装载失败。

扫描二维码关注公众号,回复: 3604772 查看本文章

      

6.1 ClassLoader类结构分析

 defineClass(byte[], int, int): 将byte字节流解析成JVM能识别的class对象。

 findClass(String): 

 loadClass(String): 获取class对象。

 resolveClass(Class<?>):

    ClassLoader是抽象类,有很多子类,如果我们要实现自己的ClassLoader,一般会继承URLClassLoader, 做修改就好了。

6.2 ClassLoader的等级加载机制

JVM提供三层ClassLoader,这三层ClassLoader可以分为两种类型

(1)Bootstrap ClassLoader: 加载JVM自身工作需要的类,完全由JVM自己控制,别人访问不到这个类,没有高一级的加载器,也没有子加载器。

(2)ExtClassLoader:

(3)AppClassLoader:

如果我们要实现自己的类加载器,不管是直接实现抽象类ClassLoader,还是继承URLClassLoader,或其他子类,它的父加载器都是AppClassLoader。

ExtClassLoader和AppClassLoader都继承了URLClassLoader类。

ClassLoader的等级加载机制

(1)JVM平台提供三层的ClassLoader,这三层ClassLoader可以分为两类,分别是服务JVM自身的,和服务广大普通类的。分别是:

  • <1>BootstrapClassLoader:主要加载JVM自身工作所需要的类,该ClassLoader没有父类加载器和子类加载器

  • <2>ExtClassLoader:这个类加载器同样是JVM自身的一部分,但是不是由JVM实现,主要用于加载System.getProperty(“java.ext.dirs”)目录地下的类,如本机的值“D:\java\jdk7\jre\lib\ext;C:\Windows\Sun\Java\lib\ext”

  • <3>AppClassLoader:加载System.getProperty("java.class.path")(注意了在ide中运行程序时,该值通常是该项目的classes文件夹)中的类。所有的自定义类加载器不管直接实现ClassLoader,是继承自URLClassLoader或其子类,其父加载器(注意:父加载器与父类的分别)都是AppClassLoader,因为不管调用哪个父类的构造器,最终都将调用getSystemClassLoader作为父加载器,而该方法返回的正是AppClassLoader。(当应用程序中没有其他自定义的classLoader,那么除了System.getProperty(“java.ext.dirs”)目录中的类,其他类都由AppClassLoader加载)

6.3 如何加载class文件

       三个步骤:

    1 找到.class文件并把这个文件包含的字节码加载到内存。

              2 字节码校验,Class类数据结构分析以及相应的内存分配和最后的符号表链接

    3 类中静态属性和初始化赋值,已经静态块的执行等。  

  6.3.1 加载字节码到内存

    URLClassLoader如何实现findclass()的,URLCclassLoader中通过一个URLClassPath类帮助取得要加载的class文件字节流,而这个URLClasspath定义了到哪里去找这个

    class文件,如果找到,再读它的byte字节流,通过definClass()来创建类对象。根据路径的不同(是文件还是jar包)来创建FIleLoader或JarLoader.

  6.3.2 验证与解析

    字节码验证,验证格式正确,行为正确。

    类准备,这个阶段准备代表每个类中定义的字段,方法和实现接口所必须的数据结构。

    解析,装入类所引用的其他所有类。

  6.3.3 初始化class对象

    执行静态初始化器,静态字段会为初始化为默认值。

6.4 常见加载类错误分析

    6.4.1 ClassNotFoundException

  显示加载类三种方法:

    Class.forName();

    ClassLoader.loadClass()

    ClassLoader.findSystemClass()   

  6.4.2 NoClassDefFoundError

    类可能没加包名。

  6.4.3 UnsatisfiedLinkError

    可能是误删了lib文件

  6.4.4 ClassCastExcetption

    JVM做类型转换的检查规则:

  •     普通对象,对象必须是目标类的实例或是目标类子类的实例。如果目标类是接口,那么可以把它当做实现了该接口的一个子类。
  •  对于数组类型,目标类型必须是数组类型或java.lang,Object, java.lang,Clonble, java.io.serializable

             如果不满足上面的规则,JVM就会报错:

              要想避免这个错误 有两种方式:

      容器类型中显示地指明容器对象类型,

      通过instanceof检查是不是目标类型,然后在强转。

  6.4.5 ExceptinInInitializerError

6.5 常用的ClassLoader分析

  这里分析了servlet的ClassLoader

6.6 如何实现自己的ClassLoader

  为什么要实现自己的classLoader

    在自己的路径下查找自定义的class文件,不一定在classPath下面。

    对我们的类做特殊处理,比如加密传输类在网络之间。这就需要在加载到JVM之前先解密。

    可以定义类的实现机制,实现热部署。

  6.6.1 加载自定义路径下的class文件

  6.6.2 加载自定义格式的class文件

6.7 实现类的热部署

  JVM在加载类之前会检查类是否已经被加载过,也就是要调用findLoadedClass()方法查看是否能返回类实例

        JVM表示两个类是否是同一个类有两个条件:

    一是看类的完整类名是否一样。

    二是看加载这个类的ClassLoader是否是同一个(是指ClassLoader实例是否是同一个)。

        实现热部署:可以用ClassLoader的两个实例加载同名的类。

6.8 java应不应该动态加载类

  JAVA修改一个类,必须重启JVM。

猜你喜欢

转载自blog.csdn.net/weixin_41395565/article/details/82976994