浅析ClassLoader工作机制

浅析ClassLoader工作机制

ClassLoader顾名思义就是类加载器,负责将Class字节码文件加载到JVM中。

如何加载class文件

ClassLoader加载一个class文件到JVM时需要经过如下步骤:

Ø  第一阶段是找到.class文件并把这个文件包含的字节码加载到内存中;

Ø  第二阶段又可以分为三个步骤,分别是字节码验证、Class类数据结构分析以及相应的内存分配和最后的符号表的链接;

Ø  第三阶段是类中静态属性和初始化赋值,以及静态块的执行等。

加载字节码到内存

通常情况下,ClassLoader加载类时,会首先调用findClass方法Class findClass(Stringname),找到我们的类的字节码文件,让后将这个文件包含的字节码加载到内存。但是在我们的ClassLoader抽象类中并没有定义如何去加载,即如何去找到指定的类并且把它的字节码加载到内存中。这个在它的子类URLClassLoader中重写了findClass()方法,在URLClassLoader中通过一个URLClassPath类帮助我们得到需要加载的class文件字节流,而这个URLClassPath定义了到哪里去找这个class文件,如果找到了这个class文件,在读取它的byte字节流,通过调用defineClass()方法来创建Class类对象。

我们在看看URLClassLoader类的构造函数,必须要传入一个URLClassPath对象,也就是必须要指定这个ClassLoader默认到哪个目录下去查找class文件。从URLClassPath的名字中可以发现,它是通过URL的形式来表示ClassPath路径的,也就是我们加载类的路径。

那么如何设置不同ClassLoader 的类加载路径的呢?如下图所示是BootStrapClassLoader、ExtClassLoader、AppClassLoader通过参数的形式指定类加载路径的:

 

BootStrapClassLoader

-Xbootclasspath:

设置BootStrap ClassLoader的搜索路径

-Xbootclasspath/a:

向后追加BootStrap ClassLoader的搜索路径

-Xbootclasspath/p:

向前追加BootStrap ClassLoader的搜索路径

ExtClassLoader

-Djava.ext.dirs=

设置ExtClassLoader的搜索路径

AppClassLoader

-Djava.class.path=
-cp=
-classpath=

设置AppClassLoader的搜索路径

 

验证与解析

Ø  字节码验证,类加载器需要对类的字节码进行需要检测,以确保格式正确、行为正确。

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

Ø  解析,在这个阶段类加载器,加载类所引用(import)的其他所有类,可以用许多方式来引用类,如extends、implements、定义属性、方法类型、方法中使用的变量类型等等。

初始化Class对象

类中包含的静态代码块都会被执行,在这个阶段的末尾静态字段会被初始化成默认值。

ClassLoader类结构分析

我们经常会用到或扩展ClassLoader,主要会用到如下几个方法,以及他们的重载方法:

Class defineClass(String name, byte[] b, intoff, int len)

用于将byte字节流解析成JVM能够识别的Class对象,有了这个方法意味着我们不仅仅能够通过class文件实例化对象,还可以通过其他方式如我们通过网络传输一个类的字节码流,拿到这个字节流我们可以创建类的Class对象,然后通过Class对象实例化类对象。

Class findClass(String name)

defineClass通常会和findClass一起使用,我们通常会直接覆盖ClassLoader父类的findClass方法来实现类的加载规则,从而取得要加载类的字节码。然后调用defineClass来生成类的Class对象。

resolveClass(Class c)

如果你想要类被加载到JVM中时就被链接(Linking上文有描述),那么可以主动的调用resolveClass方法。当然我们也可以选择让JVM来决定何时链接这个类。

Class loadClass(String name)

如果你不想重新定义类的加载规则,也没有复杂的处理逻辑,只想在运行时加载自己指定的一个类而已,那么我们此时就可以直接调用ClassLoader的loadClass方法以获取类的Class对象。这个loadClass也有重载方法,我们同样可以决定在什么时候解析这个类。

ClassLoader是一个抽象类,我们想要定义自己的ClassLoader,一般不会继承ClassLoader,而是继承URLClassLoader,因为这个类已经帮助我们实现了大部分工作。

ClassLoader的等级加载机制

JVM在加载类时默认采用的是双亲委派(或全盘委托)机制。通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。

整个JVM中提供了三个ClassLoader类,这个三个ClassLoader可以分为两种类型:BootStrapClassLoader属于一类,其他的属于一类。

BootStrap ClassLoader

它主要加载JVM自身工作需要的类,这个ClassLoader完全是自己控制的,需要加载哪个类,怎么加载都由JVM自己控制,别人也访问不到这个类,所以这个ClassLoader是不遵循上文说的加载规则的,它仅仅就是一个类加载器而已,即没有父加载器,也没有子加载器。

ExtClassLoader

这个类加载器有点特殊,它是JVM自身的一部分,但是它的血统不是很纯正,它并不是JVM亲自实现的。它加载的特定类在System.getProperties(“java.ext.dirs”)目录下。

AppClassLoader

这个类加载器就是用来专门加载应用类的,它的父类是ExtClassLoader。所有在System.getProperties(“java.class.path”)目录下的所有的类都由它来加载,这个目录就是我们常用的classpath。

注意我们如果要实现自定义的类加载器,不管你是直接继承ClassLoader,还是继承URLClassLoader类,或者其他子类,它的父加载器都是AppClassLoader,因为不管调用哪个父构造器,创建的对象都必须最终调用getSystemClassLoader()来作为我们的父类加载器,而该方法获得的恰好就是AppClassLoader。

注意:很多文章在介绍ClassLoader的时候,将BootStrapClassLoader列为我们ExtClassLoader的父加载器,其实BootStrapClassLoader并不属于JVM的类加载器等级层次,因为它并没有遵守类加载规则,另外它也没有子加载器。

 


你可能感兴趣的:(浅析ClassLoader工作机制)