设为首页 收藏本站
查看: 1086|回复: 0

[经验分享] 浅析java类加载器ClassLoader

[复制链接]

尚未签到

发表于 2017-3-1 08:07:15 | 显示全部楼层 |阅读模式
  作为一枚java猿,了解类加载器是有必要的,无论是针对面试还是自我学习。
  本文从JDK提供的ClassLoader、委托模型以及如何编写自定义的ClassLoader三方面对ClassLoader做一个简要的总结。

JDK中提供的ClassLoader
  1. Bootstrap ClassLoader
  Bootstrap加载器是用C++语言写的,它是在Java虚拟机启动后初始化的,它主要负责加载%JAVA_HOME%/jre/lib以及%JAVA_HOME%/jre/classes中的类,是最顶级的ClassLoader。
  2. Ext ClassLoader
  Ext ClassLoader是用java写的,且它的父加载器是Bootstrap,具体来说就是sun.misc.Launcher$ExtClassLoader,Ext ClassLoader主要加载%JAVA_HOME%/jre/lib/ext,此路径下的所有classes目录以及java.ext.dirs系统变量指定的路径中的类库。
  3. App ClassLoader
  系统类加载器,负责加载应用程序classpath目录下的所有jar和class文件。它的父加载器为Ext ClassLoader。
  具体关系如下图:
DSC0000.png


委托模型
  进入官方的Java doc里看到ClassLoader类的一段说明:
  The ClassLoader class uses a delegation model to search for classes and resources. Each instance of ClassLoader has an associated parent class loader. When requested to find a class or resource, a ClassLoader instance will delegate the search for the class or resource to its parent class loader before attempting to find the class or resource itself. The virtual machine's built-in class loader, called the "bootstrap class loader", does not itself have a parent but may serve as the parent of a ClassLoader instance.
  ClassLoader类使用一种委托模型来查找类和资源。每个ClassLoader实例都会关联1个父ClassLoader。当需要查询类和资源的时候,一个ClassLoader实例在查询类或资源之前会先委托给它的父ClassLoader去查询。Bootstrap ClassLoader是最顶层的加载器,并且可以作为其它ClassLoader实例的父ClassLoader。
  由此看见,这个“委托模型”的安全性是很高的,Bootstrap是最顶层的加载器,这样比如加载 java.lang.String 的时候,永远都会被Bootstrap加载(Bootstrap ClassLoader会加载%JAVA_HOME%/jre/lib中rt.jar里的String类)。 这样用户自定义的java.lang.String永远都不会被加载,这样就避免了多个java.lang.String造成的混乱现象。
  下面通过jdk里的ClassLoader源码来验证一下查找过程:

  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); //没有父加载器的话使用bootstrap加载
}
} 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;
}

  通过代码看,这里的查找过程符合委托模型。

如何编写自定义的ClassLoader
  编写自定义的ClassLoader注意2点即可:
  1. 想遵循委托模型的话重写findClass方法即可。
  2. 不遵循委托模型的话重写loadClass。
  其他:defineClass方法把字节数组b中的内容转换成Java 类,返回的结果是 java.lang.Class类的实例。这个方法被声明为final的。该方法也是jvm预留给我们处理ClassLoader与类文件关系的入口。

    protected final Class<?> defineClass(String name, byte[] b, int off, int len)
throws ClassFormatError

  下面就来写一个基于文件系统的ClassLoader:
  先来看下定义的一个类:

package org.format.classloader;
public class Obj {
@Override
public String toString() {
return "org.format.classloader.Obj";
}
}
  自定义的ClassLoader:

public class FileSystemClassLoader extends ClassLoader{
private String directory;
public FileSystemClassLoader(String directory) {
this.directory = directory;   
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] clsBytes = getClassBytes(name);
ifclsBytes == null)
throw new ClassNotFoundException();
return defineClass(name, clsBytes, 0, clsBytes.length);
}
private byte[] getClassBytes(String name) {
String location = getClassLoc(name);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
FileInputStream fis = null;
try {
fis = new FileInputStream(location);
byte[] buffer = new byte[4096];
int readLen = 0;
while( (readLen = fis.read(buffer)) != -1 ) {
baos.write(buffer, 0, readLen);
}
baos.flush();
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
private String getClassLoc(String name) {
return this.directory + File.separatorChar + name.replace('.', File.separatorChar) + ".class";
}
}
  在测试之前,使用javac命令将Obj.java编译成Obj.class,然后放与/tmp/java/classloader目录下。
  下面就用自定义的ClassLoader来进行几个测试:

FileSystemClassLoader fscl = new FileSystemClassLoader("/tmp/java/classloader");
try {
System.out.println(fscl.loadClass("org.format.classloader.Obj").newInstance());
} catch (Exception e) {
e.printStackTrace();
}   

DSC0001.png

  很明显,自定义的FileSystemClassLoader加载到了自定义的Obj类。

FileSystemClassLoader fscl1 = new FileSystemClassLoader("/tmp/java/classloader");
FileSystemClassLoader fscl2 = new FileSystemClassLoader("/tmp/java/classloader");
try {
Class cls1 = fscl1.loadClass("org.format.classloader.Obj");
Class cls2 = fscl2.loadClass("org.format.classloader.Obj");
System.out.println("class1: " + cls1);
System.out.println("class2: " + cls2);
System.out.println("class1 == class2? " + (cls1 == cls2));
} catch (Exception e) {
e.printStackTrace();
}   

DSC0002.png

  我们可以,使用不同的类加载器加载同一class文件得出的Class对象是不一样的。
  这是因为Java 虚拟机不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者都相同的情况,才认为两个类是相同的。

参考资料
  http://imtiger.net/blog/2009/11/09/java-classloader/
  http://www.ibm.com/developerworks/cn/java/j-lo-classloader/
  http://jiangbo.me/blog/2012/02/14/jetty-classloader/

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-348533-1-1.html 上篇帖子: Mesos 入门教程 下篇帖子: MapReduce任务参数调优(转)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表