java类加载器行为[笔记]

1. Java虚拟机使用第一个类的第一件事情就是将该类的字节码装进来,装载类字节码的功能是由类装器完成的,类半遮器负责根据一个类的名称来定位和生成类的字节码数据后返回给Java虚拟机。
2. 类装载器本身也是一个Java类,Java虚拟机也允许开发人员编写自己的类装载器,以便通过其他种特殊方式来产生类字节码。
3. 不管类装载器采用什么方式,只要能够在内存中制造出给Java虚拟调用的类字节码即可,所以,把类装载器描述为类字节的制造器更容易让人理解。
4. 当一个类被加载后,Java虚拟机将其编译为可执行代码并存储在内存,并将索引信息存储进一个Hashtable中,其索引关键字为与之相对应的类名。
5. Java类库中提供了一个java.lang.ClassLoader来作为类装载器的基类,Java虚拟机和程序都调用ClassLoader类的loadClass方法来加载类,ClassLoader是一个抽象类,真正的类装载器必须是ClassLoader类的子类。
6. Class类中定义了一个getClassLoader方法,用于返回它所描述的类的类加载器对象,这个返回对象的类型就是ClassLoader。

类装载器的基本策略
1. 一个类装载器本身也是一个Java类,所以,类装载器自身也需要被另外一个类装载器装载。
2. Java虚拟机中内嵌了一个称为Bootstrap的类装载器,它属于Java虚拟机的内核,不用类装载器装载。Bootstrap类装载器负责加载Java核心包中的类(即rt.jar文件中的类),这些类的Class.getClassLoader方法返回值为null,即表示是Bootstrap类装载器。
3.ExtClassLoader类装载器负责加载存放在<JAVA_HOME>/jre/lib/ext目录下的jar包中的类,AppClassLoader负责加载应用程序的启动执行类,即含有main方法的类,AppClassLoader是应用程序的第一个类装载器。


类装载器的委托模式
1. 一个Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象,如果没有指定的话则以ClassLoader.getSystemClassLoader()方法返回的系统类装载器作为其父级类装载器对象。
2. 系统类装载器通常被设置为启动应用程序的AppClassLoader,可以通过java.system.class.loader系统属性将系统类装载器设置为其它类装载器。ExtClassLoader是AppClassLoader的父级类装载器,ExtClassLoader没有父级类装载器,其实ExtClassLoader的父级类装载器是Bootstrap类装载器,但因Bootstrap不是一个Java类,它是Java虚拟机的内核,所以就干脆说ExtClassLoader没有父级类装载器。
3. 每个ClassLoader本身只能分别加载特定位置和目录中的类,但是,ClassLoader被设计成了一种委托模式,使得某一个ClassLoader可以委托它的父级类装载器去加载类,从而让应用程序可以借助某一个子级的ClassLoader去多个位置和目录中进行类的加载。
4. 当要加载一个类时,CLassLoader的loadClass方法先查找这个类是否已被装载,如果没有加载则委托其父级类装载器去加载这个类,如果父级类装载器无法装载这个类,子级类装载器才调用自己内容的findClass方法去真正加载。委托过程会一直坐收追溯到Bootstrap类装载器,如果Bootstrap类装载器也不能加载到类的话就会退回给子级类装载器直到发启装载的那个类装载器。如果委托过程中的所有类装载器都不能完成类的装载,最终就会报告ClassNotFoundException异常。
5. 一个类装载器只能创建某个类的一份字节码数据,即只能为某个类创建一个与之对应的Class实例对象。在一个Java虚拟机中可以存在多个类加载器,第个类加载器都拥有自己的名称空间,对于同一个类,每个类加载器都可以创建出它的一个Class实例对象。如果两个加载器加载了同一个类,就会实例化出了两个这个类的实例,但这个实例对象没有任何关系。
6. 采用委托模式避免了一个Java虚拟机中的多个类装载器为同一个类创建多份字节码数据的情况。只要开发人员自定义的类装载器不覆盖ClassLoader的loadClass方法,而是覆盖其findClass方法,这样就可以继续采用委托模式。因为loadClass方法所做的操作只是说去委托给其父装载器进行类的加载,findClass方法才是进行加装.class文件的加载操作,当父级装载器不能装载的情况下就会调用自己的findClass方法去加载字节码数据。


线程中的类装载器
1. 如果在类A中使用new关键字创建类B,Java虚拟机将使用加载类A的装载器来加载类B。如果在一个类中调用Class.forName方法来动态加载另外一个类,可以通过传递Class.forName方法的一个参数来指定另外那个类的类装载器,如果没有指定该参数,则使用加载当前类的类装载器。
2. 依据一个类的存放位置,这个类最终只能由一个特定的类装载器装载。对于一个已被父级类装载器装载的类来说,Java虚拟机默认也使用这个父级类装载器去装载它所调用的其他类,由于父级类装载器不会委托子级类装载器去装载类,所以在一般情况下,一个已被父级类装载器装载的类无法调用那些只能被子级类装载器去发现和装载的其他类。
3. 每个运行后线程都有一个关联的上下文类装载器,可以使用Thread.setContextClassLoader()方法设置线程的上下文类装载器。
4. 子线程默认的上下文类装载器是父线程的上下文类装载器,而主线程的类装载器初始被设置为ClassLoader.getSystemClassLoader()方法返回的系统类加载器。
5. 当线程中运行的代码需要使用某个类时,它使用上下文类装载器来装载这个类,上下文类装载器首先会委托它的父级类装载器来装载这个类,如果父级类装载器无法装载时,上下文类装载器才自己进行装载。


Tomcat5中的类加载器

Tomcat中的线程上下文类装载器为它们各自的Webbap类装载器。
1. Bootstrap为Java虚拟机内嵌的类装载器与ExtClassLoader的总称,负责加载Java核心包中的类和存放在<JAVA_HOME>/jre/lib/ext目录下的jar包中的类。
2.System即系统类装载器,通常情况下就是AppClassLoader,负责加载CLASSPATH环境变量设置的目录中的类。Tomcat不会继承操作系统上原来设置好的CLASSPATH环境变量的内容,而是将CLASSPATH环境变量重新设置为仅包含如下两个jar包:
<CATALINA_HOME>/bin/bootstrap.jar
<JAVA_HOME>/lib/tools.jar
3. Common类装载器负责从<CATALINA_HOME>/common/classes中的.class类文件和<CATALINA_HOME>/common/lib中的jar包加载类。
4. Catalina类装载器负责从<CATALINA_HOME>/server/classes中的.class类文件和<CATALINA_HOME>/server/lib中的jar包加载类
5.Shared类装载器负责从<CATALINA_HOME>/share/classes中的.class类文件和<CATALINA_HOME>/share/lib中的jar包加载类。
6.WebappX类装载器负责从当前Web应用程序的/WEB-INF/classes中的.class类文件和/WEB-INF/lib中的jar包中加载类。

Tomcat6中的类加载器与Tomcat5有了一些变化,少了一些类加载器,下面是Tomcat6中的类加载器树状图:
少了catalina与shared类加载器,从Tomcat6中的目录结构也可以看出到,6中已经没有shared,common,server文件夹了,至少Tomcat6中这些类加载器具体加载哪些jar,可参看Tomcat6的docs。
原文地址:https://www.cnblogs.com/pangblog/p/3339620.html