您的位置:首页 > 编程语言 > Java开发

[Java]ClassLoader

2014-06-09 11:34 225 查看
在Java程序员的角度来看有三种类加载器:

bootstrap:加载JAVA_HOME/lib目录下的类
extension:加载JAVA_HOME/lib/ext目录下的类
application:加载classpath指定目录中的类

在JVM中,根据ClassLoader来决定是不是同一个类,从这个角度来看,ClassLoader的作用类似namespace,也就是说可以利用ClassLoader对Class进行隔离。而如果这么用可能就要破坏双亲委派模式了。

双亲委派

从代码里面看双亲委派模型更直观些:
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
// 首先检查是否加载过该类
Class c = findLoadedClass(name);
if (c == null) {
// 先让parent去加载
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
}
// 如果parent没有加载到,自己再去加载
if (c == null) {
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
自己定义ClassLoader的话可以实现findClass方法:
protected Class<?> findClass(String name) {
// 加载不是我们关心的类时,按照原来的逻辑走
if (name.startsWith("java")) {
try {
return super.loadClass(name);
} catch (ClassNotFoundException e) {
}
}
// 从dir/XXX.class文件读取数据
byte[] data = loadClassData(name);
// 根据byte[]生成Class
return defineClass(name, data, 0, data.length);
}
下面对自己写的ClassLoader进行测试:
public class Main {
public static void main(String[] args) throws Exception {
MyClassLoader loaderA = new MyClassLoader();
MyClassLoader loaderB = new MyClassLoader();
Class<?> classA = loaderA.findClass("TestClass");
Class<?> classB = loaderB.findClass("TestClass");
System.out.println(classA == classB);// false
}
}
可以看到不同的两个ClassLoader加载相同的Class,在JVM中判断为不是同一个类。这样可能导致一个问题:用Grovy等工具的时候用ClassLoader加载类,可能会有大量的ClassLoader加载大量的类并没有被及时回收导致OOM。

命名空间

由不同的类装载器加载的类被放在虚拟机内部不同的命名空间中。同一个命名空间中的类可以直接进行交互,而不会感知到另一个命名空间中类的存在。这里有两个概念:

初始类装载器:如果要求A去装载一个类型,而A返回了B装载的类型,那么A为就是初始类装载器。
定义类装载器:B是定义类装载器。

如果某个类加载器把加载的任务委派给另一个类加载器,而后者定义了这个类型,那么被委派的类加载器装载的这个类型,在所有被记为该类型的初始类装载器的命名空间中共享。

----------END----------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: