java classloader
来源:互联网 发布:大麦盒子电视直播软件 编辑:程序博客网 时间:2023/06/07 06:54
一、classLoader加载机制
JVM类加载器加载机制并不是继承关系,而是委派关系,之前意识中一直当作是继承关系。自定义classloader的时候,通常会传一个parent classLoader,看见parent就想当然的理解成继承关系,直到在写code时,发现不同classLoader的用各自的parent加载的class,都是用的相同的classLoader实例,然后才把这个潜意识改变过来。
1.默认
JVM默认类加载机制是自下而上的委派方式,即加载某个class时,先从parent classLoader加载,如果该parent还有parent,则依次向上递归,若还加载不到,此时自己再加载。例如,我们通过命令行+classpath参数启动的程序,就是该流程,下面引用java.lang.ClassLoader的loadClass方法代码:
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // 查看是否被加载过 Class c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try {//若parent loader不空,在委托parent加载 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) { // 若还没有加载到,此时自己再加载 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; } }
2.定制
某些场合下,优先从本地加载class,若加载不到,再委托父类加载。 JavaEE规范则推荐每个类加载模块先加载本类加载的内容,若果加载不到,才尝试从parent中加载。比如tomcat,jetty就是采用这种加载机制。下面贴出jetty的webAppClassLoader的加载class片段:
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { Class<?> c= findLoadedClass(name); ClassNotFoundException ex= null; boolean tried_parent= false; //判断是否是系统类,即AppClassLoader加载的 boolean system_class=_context.isSystemClass(name); //判断是否是jetty本身类,即jetty代码 boolean server_class=_context.isServerClass(name); if (system_class && server_class) { return null; } //isParentLoaderPriority标示是否先委托parent加载,默认false if (c == null && _parent!=null && (_context.isParentLoaderPriority() || system_class) && !server_class) { tried_parent= true; try { c= _parent.loadClass(name); if (LOG.isDebugEnabled()) LOG.debug("loaded " + c); } catch (ClassNotFoundException e) { ex= e; } } if (c == null) { try { //自己加载各个war包下的类 c= this.findClass(name); } catch (ClassNotFoundException e) { ex= e; } } if (c == null && _parent!=null && !tried_parent && !server_class ) c= _parent.loadClass(name); if (c == null) if (resolve) resolveClass(c); if (LOG.isDebugEnabled()) LOG.debug("loaded " + c+ " from "+c.getClassLoader()); return c; }
二、classLoader加载体系
第一节中提到2种类加载机制,一个种是自下而上的委派模式,另外一种则是定制,优先从本地类加载模块加载。下面就介绍几种类加载器:
1.bootstrap classLoader
bootstrap加载器是用C++语言写的,它是在Java虚拟机启动后初始化的,它主要负责加载%JAVA_HOME%/jre/lib,-Xbootclasspath参数指定的路径以及%JAVA_HOME%/jre/classes中的类。
2.extension classLoader
Bootstrap loader加载ExtClassLoader,并且将ExtClassLoader的父加载器设置为Bootstrap loader。ExtClassLoader是用Java写的,具体来说就是 sun.misc.Launcher$ExtClassLoader,ExtClassLoader主要加载%JAVA_HOME%/jre/lib/ext,此路径下的所有classes目录以及java.ext.dirs系统变量指定的路径中类库。
3.system app classLoader
AppClassLoader也是用Java写成的,它的实现类是 sun.misc.Launcher$AppClassLoader,另外我们知道ClassLoader中有个getSystemClassLoader方法,此方法返回的正是AppclassLoader.AppClassLoader主要负责加载classpath所指定的位置的类或者是jar文档,它也是Java程序默认的类加载器。
4.customer classLoader
就是开发自定义的classLoader,通常它的parent classLoader一般是AppClassLoader。
三、classLoader实践与样例
在开发中常见的几种classLoader错误
1.NoClassDefFoundError
classloader没有找到该类的定义,一般是该类的jar包没有加载到classpath路径下。
2.NoSuchMethodError
NoSuchMethodError情况在开发过程中也会经常遇到。在项目中该类所在的jar包有多个版本,比如maven工程中,jar包版本的间接依赖导致版本仲裁的时候,选择的版本可能并不是我们所需要的版本,就可能会出现这个错误,通过maven tree把jar包依赖树打印出来排除掉即可。
3.ClassCastException
在JVM中,确定一个类的实例类型是:类全名+类加载器。那么出现ClassCastException一般是类型转换的时候遇到问题,即class实例类型不对。在双亲委派模式下一般不会出现,classLoader都交给系统处理。当遇到我们自定义的classLoader的时候,就可能会出现这种问题。
4.LinkageError
通常这种错误比较难排查,其实和ClassCastException类型本质一样,下面是我摘自他人的一个样例:
public class HandleUtils { public void m(Param param) { param.generate(); } public void print(){ System.out.println("hello world!"); }}
public class Param { private String name; public String getName() { return this.name; } public void setName(String name) { this.name = name; } public Param(){ System.out.println("Param: "+Param.class.getClassLoader()); } public Param2 generate() { System.out.println("Param2: "+Param2.class.getClassLoader()); return new Param2(); }// public Param generate() {// return new Param();// }}
public class Param2 extends Param { public Param2 generate() { System.out.println("i am param2."); return new Param2(); }}
public class TestLinkError { public static void main(String[] args) { try { TestLinkError.test(); } catch (Exception e) { e.printStackTrace();// System.out.println(e); } } public static void test() throws Exception { // cl1在加载HandleUtils和Param时将会使用AppClassLoader URLClassLoader cl1 = new URLClassLoader(new URL[] { new File("target/test-classes").toURI().toURL() }, null) { @Override public Class<?> loadClass(String name) throws ClassNotFoundException { if ("com.murdock.classloader.linkageerror.HandleUtils".equals(name)) { return ClassLoader.getSystemClassLoader().loadClass(name); } if ("com.murdock.classloader.linkageerror.Param".equals(name)) { return ClassLoader.getSystemClassLoader().loadClass(name); } return super.loadClass(name); } }; ClassLoader.getSystemClassLoader().loadClass("com.murdock.classloader.linkageerror.Param2"); HandleUtils hu = (HandleUtils) cl1.loadClass("com.murdock.classloader.linkageerror.HandleUtils").newInstance(); hu.m((Param) cl1.loadClass("com.murdock.classloader.linkageerror.Param2").newInstance()); }
四、其他
- 类加载器方面的文章 http://zeroturnaround.com/rebellabs/rebel-labs-tutorial-do-you-really-get-classloaders/4/
- java ClassLoader
- JAVA classLoader
- java ClassLoader
- Java ClassLoader
- Java ClassLoader
- java ClassLoader
- java classloader
- Java ClassLoader
- java ClassLoader
- java classLoader
- Java Classloader
- Java ClassLoader
- Java ClassLoader
- java classloader
- Java Classloader
- java classloader
- java classLoader
- Java ClassLoader
- xcode7制作framework(包含加入xib,资源文件等)
- 杭电acm 4561(连续最大积的相关题目)
- hdoj--1408--盐水的故事(技巧)
- xml文件解析类及Android项目中常用到的两种获取xml文件方式
- Linux命令简介—pwd
- java classloader
- Oracle 中使用fetch bulk collect into 批量效率的读取游标数据
- 腾讯Bugly干货分享:Android应用性能评测调优
- hdu 4512 吉哥系列故事——完美队形I(LICS)
- svn 常用命令
- 立体视觉——固定窗口的视差图计算
- 树莓派添加RTC时钟模块的方法
- 浅谈协方差矩阵
- maxProfit