Java类加载器(Java Classloader)是Java运行时环境(Java Runtime Environment)的一部分,负责动态加载Java类到Java虚拟机的内存空间中。
对于HotSpot虚拟机而言,类加载器大体分为两种:属于JVM本身的类加载器,如:Bootstrap ClassLoader;不属于JVM本身的类加载器,如:Extensions ClassLoader、Application ClassLoader、用户自定义类加载器:
HotSpot虚拟机(由C、C++等语言编写)本身包含了一个名为Bootstrap ClassLoader的类加载器;BootstrapClassLoader是用C++编写的,它负责将核心JavaClass加载到虚拟机内存中。
注:核心JavaClass指:
1、
2、被-Xbootclasspath参数指定的路径中的 且被拟机识别的类库。
注:在编写自定义的加载器时,并不能被显示的指定parent为Bootstrap ClassLoader,如果想把parent设置为引导类
加载器,那么直接使parent值为null即可;或者直接就想用引导类加载器的话,直接使该加载器为null即可。
此加载器本身由BootstrapClassLoader加载;负责加载扩展的Javaclass。该类处于sun.misc.Launcher$ExtClassLoader。
注:扩展JavaClass指:
1、
2、系统变量java.ext.dirs指定的目录下的所有类库。
此加载器本身由BootstrapClassLoader加载;负责加载Java应用程序类路径(classpath路径)下的类库。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。该类处于sun.misc.Launcher$AppClassLoader。
最基本的加载流程:
当运行一个程序的时候,JVM启动,运行BootstrapClassLoader,该ClassLoader加载java核心API(ExtClassLoader和AppClassLoader也在此时被加载),然后调用ExtClassLoader加载扩展API,最后AppClassLoader加载CLASSPATH目录下定义的Class,这就是一个程序最基本的加载流程。
测试ClassLoader的parent属性:
注:类加载器之间的类关系是关联而不是泛化(详细点说:是组合而不是继承)。AppClassLoader的parent是
ExtClassLoader,ExtClassLoader的parent是null(注:当一个ClassLoader的parent为null时,就说明这个
类加载器的parent是Bootstrap ClassLoader)。
假设现在用户自定义了一个类加载器:
注:自定义类加载器,需要继承ClassLoader,并重写findClass方法。
追注:JDK1.2之后就不推荐重写loadClass方法了,而是推荐重写findClass方法,在
protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException
方法里会调用protected Class> findClass(String name) throws ClassNotFoundException方法,
详见源码。
那么加载一个类的流程为:
注:双亲委派模式很好的解决了各个加载器对基础类的加载的问题(越基础的类,越上层加载),保证了基础类库的安
全性。
双亲委派模式只是类加载器设计者推荐的一种模式,并不是一定要遵循的。在某些场景里,可能或用到直接加载某些类。
如JNDI(即:Java Naming and Directory Interface),JNDI是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,由不同的独立厂商来对这些接口进行各自的实现,由管理者将JNDI API映射为特定的命名服务和目录系统,使得Java应用程序可以和这些命名服务和目录服务之间进行交互。
JNDI位于rt.jar下,由Bootstrap ClassLoader加载,JNDI的目的是对资源进行集中管理和查找,JNDI需要调用由第三方独立厂商提供并部署在应用程序的classpath下SPI(Service Provider Interface)实现类。因为Bootstrap ClassLoader并不能加载这些类,为了解决这个问题,Java设计团队就引入了Thread Context ClassLoader线程上下文类加载器。这样一来Bootstrap ClassLoader加载JNDI后,JNDI再通过Thread Context ClassLoader去加载对应的类到虚拟机内存(注:这样其实就打破了双亲委托原则了)。
注:Thread Context ClassLoader可以通过 java.lang.Thread类的setContextClassLoaser()方法进行设置;如果
创建线程时还未设置,它将会从父线程中继承个,如果在应用程序的全局范围内都没有设置过的话,那这个类加
载器默认就是AppClassLoader。
随着用户对程序动态性(如:代码热替换、模块儿热部署)的追求,类加载又有进一步的演变。OSGI(Open Service Gateway Initiative)技术是Java动态化模块化系统的一系列规范。在OSGI环境下,类加载器不再是双亲委派模型中的树状结构,而是进一步发展为更加复杂的网状结构,当收到类加载请求时,OSG将按照下面的顺序进行类搜索:
注上述步骤中1、2步走的还是双亲委派模式。
声明:本人部分内容直接摘录自《深入理解Java虚拟机·JVM高级特性与最佳实践》 周志明著。