ClassLoader浅析

类加载的方式有2种。

显示类加载

ClassLoader.loadClass()(使用指定的Classloader进行装载)

Class.forName()(使用当前类的Caller Classloader进行装载)

隐示类加载(发生在由于引用、实例化或继承导致需要装载类的时候。隐式类装载是在幕后启动的,JVM会解析必要的引用并装载类。)

 

Classloader可能先显式地装载一个类,然后再隐式地装载它引用的其它类。

从类装载方式的描述中我们可以看到, 只有在显式的调用方法或者实例化、引用、继承一个类时,类才真正被装载 。由此,我们可以知道,import并不会导致类装载,以及,在一个类实例化之前,调用它的静态方法,会导致这个类和它的父类、实现的接口和相关的静态成员的类会被装载,而它的成员变量的类却不会被装载

ClassLoader中有个ClassLoader类型的parent的属性,表示需要loadClass的时候先找parent加载。

这跟职责链模式有点类似,职责链是自己处理不了就交给下一个处理器处理。而在ClassLoader中,是先找到自己的parent加载类,递归地找到最高层次的parent,如果不成功,再让本ClassLoader进行类加载。如果parent为null,则认为是JVM中的BootStrapClassLoader。

在JVM启动后,类的加载方式有2种,一种是预先加入(pre-loading),另外一种是按需载入(load-on-demand)。

 

 

如图,在JVM启动以后首先启动BootStrapClassLoader,BootStrapClassLoader开始加载核心API类,在加载sun.misc下的Launcher.java以后加载2个内部类。

第一个是ExtClassLoader,负责把Java的扩展库载入。同时将ExtClassLoader的parent设为null,也就是BootStrapClassLoader。

第二个是AppClassLoader,负责把CLASSPATH路径下的类库和用户自定义类加载到内存中。其parent是ExtClassLoader。

 

 

类的加载器可以用Class.getClassLoader()方法来获得。如下示例:

 

System.out.println(App.class.getClassLoader());//App是自定义类
System.out.println(String.class.getClassLoader());

 

返回结果是:

 

sun.misc.Launcher$AppClassLoader@19821f
null

 

 

数组类的Class对象不是由类加载器创建的,而是由Java运行时根据需要自动创建。如果是基本数据类型,则没有类加载器。

自定义的ClassLoader可以继承URLClassLoader或ClassLoader。当继承ClassLoader重写findClass()方法,parent会相应是AppClassLoader-->ExtClassLoader-->BootStrapClassLoader。

只有在实例化一个类的时候才会被ClassLoader载入,仅仅申明并不会。

ClassLoader 中最关键的loadClass()源码: 

protected synchronized Class<?> loadClass(String name, boolean resolve)
			throws ClassNotFoundException {
		// First, check if the class has already been loaded
		Class c = findLoadedClass(name);
		if (c == null) {
			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.
				c = findClass(name);
			}
		}
		if (resolve) {
			resolveClass(c);
		}
		return c;
	}

 

可以看见第一步是先查找该类是否已经加载,如果未加载,查看parent是否为空,不空的话就交给parent处理,空的话就交给 findBootstrapClassOrNull()方法,该方法让BootStrapClassLoader加载类。如果从parent那里加载失败 (抛出异常了),那么就让自己加载类(调用本类被继承的方法findClass())。之后判断resolve,如果为真就 连接。

说到连接,参考《深入Java虚拟机》。

类装载器子系统除了要定位和导入二进制文件外,还必须确认验证被导入类的正确性,为类变量分配并初始化内存。这些动作的顺序是:1)装载;2)连接:a.验证;b.准备;c.初始化;3)初始化。

 

 

 

装载就是查找并将二进制数据装到虚拟机中。

连接开始时执行验证,验证后就准备,准备的过程是为类变量(静态变量)分配内存并将其初始化为默认值。解析这一步是可选的,是将类型中的符号引用转换为直接引用。之后进行初始化,这时候把类变量初始化为正确的值(有进行引用赋值的情况下)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值