JVM类加载机制与Tomcat类加载机制

1. JVM类加载机制

JVM的ClassLoader通过Parent属性定义父子关系,可以形成树状结构。其中引导类、扩展类、系统类三个加载器是JVM内置的

它们的作用分别是:

BootstrapClassLoader引导类加载器:---加载/jdk1.7.0_67/jre/目录下的核心库

ExtClassLoader拓展类加载器:---加载/jdk1.7.0_67/jre/lib/ext/目录下的扩展包

AppClassLoader系统类加载器:---加载CLASSPATH路径下的包

可用以下代码验证:

import java.net.URL;
import java.net.URLClassLoader;

/*
分析BootstrapClassLoader/ExtClassLoader/AppClassLoader的加载路径
*
*/

public class ClassPath_of_Bootstrap_Ext_AppClassLoader
{
	public static void main(String[] args)
	{
		System.out.println("BootstrapClassLoader 的加载路径: ");
		
		URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
		for(URL url : urls)
			System.out.println(url);
		System.out.println("----------------------------");
				
		//取得扩展类加载器
		URLClassLoader extClassLoader = (URLClassLoader)ClassLoader.getSystemClassLoader().getParent();

		System.out.println(extClassLoader);
		System.out.println("扩展类加载器 的加载路径: ");
		
		urls = extClassLoader.getURLs();
		for(URL url : urls)
			System.out.println(url);
		
		System.out.println("----------------------------");
				
		
		//取得应用(系统)类加载器
		URLClassLoader appClassLoader = (URLClassLoader)ClassLoader.getSystemClassLoader();
		
		System.out.println(appClassLoader);
		System.out.println("应用(系统)类加载器 的加载路径: ");
		
		urls = appClassLoader.getURLs();
		for(URL url : urls)
			System.out.println(url);
				
		System.out.println("----------------------------");	
	}
}
BootstrapClassLoader 的加载路径: 
file:/D:/app/Java/jdk1.7.0_67/jre/lib/resources.jar
file:/D:/app/Java/jdk1.7.0_67/jre/lib/rt.jar
file:/D:/app/Java/jdk1.7.0_67/jre/lib/sunrsasign.jar
file:/D:/app/Java/jdk1.7.0_67/jre/lib/jsse.jar
file:/D:/app/Java/jdk1.7.0_67/jre/lib/jce.jar
file:/D:/app/Java/jdk1.7.0_67/jre/lib/charsets.jar
file:/D:/app/Java/jdk1.7.0_67/jre/lib/jfr.jar
file:/D:/app/Java/jdk1.7.0_67/jre/classes
----------------------------
sun.misc.Launcher$ExtClassLoader@d9660d
扩展类加载器 的加载路径: 
file:/D:/app/Java/jdk1.7.0_67/jre/lib/ext/access-bridge.jar
file:/D:/app/Java/jdk1.7.0_67/jre/lib/ext/dnsns.jar
file:/D:/app/Java/jdk1.7.0_67/jre/lib/ext/jaccess.jar
file:/D:/app/Java/jdk1.7.0_67/jre/lib/ext/localedata.jar
file:/D:/app/Java/jdk1.7.0_67/jre/lib/ext/sunec.jar
file:/D:/app/Java/jdk1.7.0_67/jre/lib/ext/sunjce_provider.jar
file:/D:/app/Java/jdk1.7.0_67/jre/lib/ext/sunmscapi.jar
file:/D:/app/Java/jdk1.7.0_67/jre/lib/ext/sunpkcs11.jar
file:/D:/app/Java/jdk1.7.0_67/jre/lib/ext/zipfs.jar
----------------------------
sun.misc.Launcher$AppClassLoader@18f1d7e
应用(系统)类加载器 的加载路径: 
file:/D:/workspace/myTestProject/build/classes/
----------------------------

JVM中各ClassLoader的树状关系:

JVM类加载机制与Tomcat类加载机制_第1张图片

2. 父委托机制或者说双亲委派模型

JDK API中指出:

protected Class<?> loadClass(String name,boolean resolve) throws ClassNotFoundException
使用指定的 二进制名称来加载类。此方法的默认实现将按以下顺序搜索类:

  1. 调用 findLoadedClass(String) 来检查是否已经加载类。

  2. 在父类加载器上调用 loadClass 方法。如果父类加载器为null,则使用虚拟机的内置类(称为“bootstrap class loader”)加载器。

  3. 调用 findClass(String) 方法查找类。


JVM类加载机制与Tomcat类加载机制_第2张图片

图例1—父委托机制

JVM加载模型的工作过程是:
     如果一个类加载器收到了类加载的请求,它不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成。
     每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的引导类加载器中。
     只有父类加载无法完成这个请求时,子类加载器才会尝试自己去加载。

JVM类加载机制与Tomcat类加载机制_第3张图片

图例2—双亲委派模型


为什么要让父类加载器优先去加载呢?试想如果子类加载器先加载,那么我们可以写一些与java.lang包中基础类同名的类,
然后再定义一个子类加载器,这样整个应用使用的基础类就都变成我们自己定义的类了。这样就有很大的安全隐患!
所以自己编写类加载器时,如果没有特殊原因,一定要遵守类加载的双亲委派模型。

3. Tomcat类加载器
Tomcat基本遵守了JVM的委派模型,但也在自定义的类加载器中做了细微的调整,以适应Tomcat自身的要求。
下面是Tomcat类加载器WebappClassLoader的核心方法loadClass()的源码。
它覆盖了父类URLClassLoader中的方法,改变了默认的类加载顺序。
public synchronized Class loadClass(String name, boolean resolve)

        throws ClassNotFoundException {

 

        Class clazz = null;

 

        // (0) Check our previously loaded local class cache

        clazz = findLoadedClass0(name);

        if (clazz != null) {

            if (log.isDebugEnabled())

                log.debug("  Returning class from cache");

            if (resolve)

                resolveClass(clazz);

            return (clazz);

        }

 

        // (0.1) Check our previously loaded class cache

        clazz = findLoadedClass(name);

        if (clazz != null) {

            if (log.isDebugEnabled())

                log.debug("  Returning class from cache");

            if (resolve)

                resolveClass(clazz);

            return (clazz);

        }

 

        // (0.2) Try loading the class with the system class loader, to prevent

        //       the webapp from overriding J2SE classes

        try {

            clazz = system.loadClass(name);

            if (clazz != null) {

                if (resolve)

                    resolveClass(clazz);

                return (clazz);

            }

        } catch (ClassNotFoundException e) {

            // Ignore

        }

 

        boolean delegateLoad = delegate || filter(name);

 

        // (1) Delegate to our parent if requested

        if (delegateLoad) {

            if (log.isDebugEnabled())

                log.debug("  Delegating to parent classloader1 " + parent);

            ClassLoader loader = parent;

            if (loader == null)

                loader = system;

            try {

                clazz = loader.loadClass(name);

                if (clazz != null) {

                    if (log.isDebugEnabled())

                        log.debug("  Loading class from parent");

                    if (resolve)

                        resolveClass(clazz);

                    return (clazz);

                }

            } catch (ClassNotFoundException e) {

                ;

            }

        }

 

        // (2) Search local repositories

        if (log.isDebugEnabled())

            log.debug("  Searching local repositories");

        try {

            clazz = findClass(name);

            if (clazz != null) {

                if (log.isDebugEnabled())

                    log.debug("  Loading class from local repository");

                if (resolve)

                    resolveClass(clazz);

                return (clazz);

            }

        } catch (ClassNotFoundException e) {

            ;

        }

 

        // (3) Delegate to parent unconditionally

        if (!delegateLoad) {

            if (log.isDebugEnabled())

                log.debug("  Delegating to parent classloader at end: " + parent);

            ClassLoader loader = parent;

            if (loader == null)

                loader = system;

            try {

                clazz = loader.loadClass(name);

                if (clazz != null) {

                    if (log.isDebugEnabled())

                        log.debug("  Loading class from parent");

                    if (resolve)

                        resolveClass(clazz);

                    return (clazz);

                }

            } catch (ClassNotFoundException e) {

                ;

            }

        }

 

        throw new ClassNotFoundException(name);

    }
具体分析一下:首先findLoadedClass0()和findLoadedClass()分别从本地和父类加载器的缓存中查找当前要加载的类是否已经加载过了。
之后为了避免上面提到的安全问题,Tomcat类加载器会将加载请求委派给系统类加载器。接下来根据delegate变量的设置,决定是先由自己加载,
还是先由父类加载器去加载。

这里介绍一下背景,上面的WebappClassLoader是对应一个Web应用的类加载器,其父亲是Tomcat的lib加载器。所以delegate变量的值,
决定了Tomcat的类加载顺序。

4. Tomcat6的加载顺序
所以在Tomcat 6中默认情况下,不是完全按照先Tomcat的lib再Web应用的lib这种顺序去加载类。
Jar包的加载顺序是:
1)JRE中的Java基础包
2)Web应用WEB-INF\lib下的包
3)Tomcat\lib下的包
如果想要在Web应用间共享一些Jar包,则不仅需要将公共包放在Tomcat的lib下,还要删掉Web应用lib中的包,
否则Tomcat启动时还是会优先加载Web应用lib下的包的。

以上内容引用了:http://blog.csdn.net/dc_726/article/details/11873343等博客内容

你可能感兴趣的:(jvm,ClassLoader,bootstrap)