【阅读笔记】深入分析JavaWeb技术内幕-(三)深入分析ClassLoader加载机制

Classloader负责将Class加载到JVM中,并且确定由那个ClassLoader来加载(父优先的等级加载机制)。还有一个任务就是将Class字节码重新解释为JVM统一要求的格式

1.Classloader类结构分析

(1)主要由四个方法,分别是defineClass,findClass,loadClass,resolveClass
  • <1>defineClass(byte[] , int ,int) 将byte字节流解析为JVM能够识别的Class对象(直接调用这个方法生成的Class对象还没有resolve,这个resolve将会在这个对象真正实例化时resolve)

  • <2>findClass,通过类名去加载对应的Class对象。当我们实现自定义的classLoader通常是重写这个方法,根据传入的类名找到对应字节码的文件,并通过调用defineClass解析出Class独享

  • <3>loadClass运行时可以通过调用此方法加载一个类(由于类是动态加载进jvm,用多少加载多少的?)

  • <4>resolveClass手动调用这个使得被加到JVM的类被链接(解析resolve这个类?)

(2)实现自定义ClassLoader一般会继承URLClassLoader类,因为这个类实现了大部分方法。

2.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加载)

(2)Jvm加载class文件到内存有两种方式,隐式加载和显示加载,通常这两种方式是混合使用的
  • <1>隐式加载:是通过JVM来自动加载需要的类到内存的方式,当某个类被使用时,JVM发现该类不在内存中,那么它就会自动加载该类到内存

  • <2>显示加载:通过调用this.getClasss.getClassLoader.loadClass(),Class.forName,自己实现的ClassLoader的findClass方法

(3)上级委托机制:当一个加载器加载类字时,先委托其父加载器加载,若加载成功则反馈给该加载器,若父加载器不能加载,则由该加载器加载

3.如何加载class文件:

分为三个步骤 加载字节码到内存、Linking、类字节初始化赋值

(1)加载字节码到内存:(这一步通常通过findclass()方法实现)

以URLClassLoader为例:该类的构造函数返现必须制定一个URL数据才能创建该对象,该类中包含一个URLClassPath对象,URLClassPath会判断传过来的URL是文件还是Jar包,创建相应的FileLoader或者JarLoader或者默认加载器,当jvm调用findclass时,这些加载器将class文件的字节码加载到内存中

(2)Linking:验证与解析,包含3步:
  • <1>字节码验证

  • <2>类准备:准备代表每个类中定义的字段、方法和实现接口所需的数据结构

  • <3>解析:这个阶段类装入器转入类所应用的其他类

(3)初始化class对象,执行静态初始化器并在这阶段末尾初始化静态字段为默认值

4.常见加载类错误分析

(1)ClassNotFoundException:

通常是jvm要加载一个文件的字节码到内存时,没有找到这些字节码(如forName,loadClass等方法)

(2)NoClassDefFoundError:

通常是使用new关键字,属性引用了某个类,继承了某个类或接口,但JVM加载这些类时发现这些类不存在的异常

(3)UnsatisfiedLinkErrpr:

如native的方法找不到本机的lib

5.常用classLoader(书本此处其实是对tom加载servlet使用的classLoader分析)

(1)AppClassLoader:

加载jvm的classpath中的类和tomcat的核心类

(2)StandardClassLoader:

加载tomcat容器的classLoader,另外webAppClassLoader在loadclass时,发现类不在JVM的classPath下,在PackageTriggers(是一个字符串数组,包含一组不能使用webAppClassLoader加载的类的包名字符串)下的话,将由该加载器加载(注意:StandardClassLoader并没有覆盖loadclass方法,所以其加载的类和AppClassLoader加载没什么分别,并且使用getClassLoader返回的也是AppClassLoader)(另外,如果web应用直接放在tomcat的webapp目录下该应用就会通过StandardClassLoader加载,估计是因为webapp目录在PackageTriggers中?)

(3)webAppClassLoader如:

Servlet等web应用中的类的加载(loadclass方法的规则详见P169)

6.自定义的classloader

(1)需要使用自定义classloader的情况
  • <1>不在System.getProperty("java.class.path")中的类文件不可以被AppClassLoader找到(LoaderClass方法只会去classpath下加载特定类名的类),当class文件的字节码不在ClassPath就需要自定义classloader

  • <2>对加载的某些类需要作特殊处理

  • <3>定义类的实效机制,对已经修改的类重新加载,实现热部署

(2)加载自定义路径中的class文件
  • <1>加载特定来源的某些类:重写find方法,使特定类或者特定来源的字节码 通过defineClass获得class类并返回(应该符合jvm的类加载规范,其他类仍使用父加载器加载)

  • <2>加载自顶一个是的class文件(如经过网络传来的经过加密的class文件字节码):findclass中加密后再加载

7.实现类的热部署:

  • (1)同一个classLoader的两个实例加载同一个类,JVM也会识别为两个

  • (2)不能重复加载同一个类(全名相同,并使用同一个类加载器),会报错

  • (3)不应该动态加载类,因为对象呗引用后,对象的属性结构被修改会引发问题

注意:使用不同classLoader加载的同一个类文件得到的类,JVM将当作是两个不同类,使用单例模式,强制类型转换时都可能因为这个原因出问题。

原书链接

以上内容只是个人笔记记录,更多完整内容请购买作者原书籍查看。《深入分析JavaWeb技术内幕》

你可能感兴趣的:(【阅读笔记】深入分析JavaWeb技术内幕-(三)深入分析ClassLoader加载机制)