[jvm解析系列][十]类加载器和双亲委派模型,你真的了解ClassLoader吗?

上一章我们讲到,一个类加载到内存里我们可以操作的部分只有两个,一个是加载部分一个是static{},我相信static{}不用多讲了。

接下来我们就来解析一下ClassLoader即类加载器,他就是用来加载字节码到方法区的类。

当年出现ClassLoader这个东西动态加载类的字节码主要还是为了满足JavaApplet的需求。虽然后来JavaApplet挂掉了,但是ClassLoader这个形式还是保留了下来,而且活的很好。

类的相等和instanceOf

来我们来写一个例子

public class ClassLoaderTest {
	public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException{
		ClassLoader loader = new ClassLoader() {
			@Override
			public Class<?> loadClass(String name) throws ClassNotFoundException {
				// TODO Auto-generated method stub
				try {
					String className = name.substring(name.lastIndexOf(".")+1)+".class";
					InputStream is = getClass().getResourceAsStream(className);
					if(is ==null){
						return super.loadClass(name);
					}
					byte[] buffer =  new byte[is.available()];
					is.read(buffer);
					return defineClass(name, buffer, 0, buffer.length);
				} catch (Exception e) {
					// TODO: handle exception
					throw new ClassNotFoundException(name);
				}
			
			}
		};
		Object object  = loader.loadClass("top.jjust.jvm.ClassLoaderTest").newInstance();
		System.out.println(object.getClass());
		System.out.println(object instanceof top.jjust.jvm.ClassLoaderTest);
	}
}
上述代码先重写了一个loadClass方法,然后用重写的方法加载了自己这个类并生成实例。

两行输出结果如下

<span style="font-size:12px;">class top.jjust.jvm.ClassLoaderTest
false</span>
我们可以看到class是没错的,但是我们用重写loadClass读取的class文件却不被认为是原类的一个子类(可能说起来比较拗口,大家可以看看代码就明白了)

这边牵扯到一个相等的问题,判断两个类是否相等(equals、isAssgnableFrom、isInstance、instanceof)有一个前提就是:这两个类由同一个类加载器加载。如果两个不同的加载器加载,判断是一定不等的。

双亲委派模型

在jvm中,有两种不一样的类加载器,

一个是加载<JAVA_HOME>/lib下的文件,也就是jvm本身的类(并且jvm会识别名字,单纯放在目录下是没用的),也就是加载自身的类加载器

还有一种是加载其他类的类加载器。这个种类的类加载器又可以细分为扩展类加载器和应用程序类加载器。

扩展类加载器主要是加载<JAVA_HOME>/lib/ext文件下的类库而应用程序类加载器主要是加载用户类路径上指定的类库,平时getsystemClassLoader就是返回的它,程序里没有定义过自己的类加载器一般情况下也是用它。这几个类加载器我们用图片表示一下。


在类中加载需要按照一种层次,这种层次我们画在下面:


这是什么意思?也就是说碰到一个类需要加载时,先要把这个请求交给父类,直到顶层,如果Bootstrap ClassLoader说我不做这个,才会由下一层尝试加载。如果所有父类都不能加载,才会自己加载。

为什么要设计这种模型呢?我们具一格例子,就拿所有的父类Object讲,这个类一般是由Bootstrap ClassLoader加载的,如果不使用双亲委派模型,在自定义加载器中加载了这个Objcet,那么一个jvm中就会出现多个Objcet。而不是像双亲委派模型一样,都由Bootstrap ClassLoader加载,不会重复加载。

其实在重写方法的时候,建议重写findClass()方法而不是loadClass,下面是loadClass的源码

 protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
我们发现,如果我们重写loadClass则会破坏双亲委派模型。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章