classloader浅谈

1.classloader的作用,主要作用加载.class文件,次要作用:加载其他格式的文件。

2. classpath:
classpath 决定了一个class loader的职能范围。
Bootstrap ClassLoader、Extension ClassLoader、System ClassLoader的区别就是其classpath不同。


3.URLClassLoader
  url不是classpath结构体系中的一个层级。他是实现一个classloader的方法。
  public URLClassLoader(URL[] urls, ClassLoader parent) ;

4 class的加载链。从A的main方法进入程序之后,A引用到的类B,由A的classLoader尝试加载。

5 双亲委派模式
  双亲委派模式不是一种语言规范,仅仅是一种实现细节。自己重写classloader的时候,完全可以打破这种模式(比如tomcat的 WebappClassLoader)

6.标准实现里,加载配置文件也是双亲委派模式。getResources相关的所有方法,最终调用的都是getResource方法。我们常用的getClassAsStream只能读取自己classpath下的配置文件。如果在父类的classpath下存在同名文件,则这个配置文件永远无法被读取。

7. 加载class的相关方法最终调用的都是loadClass方法。 加载class其实也是加载一个资源,只不过发现后缀是.class,就如何如何,重要的方法包括defineClass,resolveClass 。

8. loader class defineClass,resolveClass 主要干下面的工作:
       装载:查找和导入类或接口的字节码;
       链接:执行下面的校验、准备和解析步骤,其中解析步骤是可以选择的;
         校验:检查导入类或接口的二进制数据的正确性;
         准备:给类的静态变量分配并初始化存储空间;
         解析:将符号引用转成直接引用;

9.A.class.getClassLoader();
  和Thread.currentThread().getContextClassLoader()是有很大的区别的。正如上文所说前者得到的classloader是明确的,就是加载A的那个loader。后者是不明确的,所有不建议使用后者的方式,实际上,getContextClassLoader()得到的结果仅仅依赖于setgetContextClassLoader()。你甚至是可以把当前线程上下文的classloader设置成Bootstrap classLoader。

10.ClassLoader.getCallerClassLoader()。 这个方法很强,就是仅仅是知道类名的时候,返   回A.class.getClassLoader()一样的效果。他是怎么做到的?

11.class.forName和 loadClass的区别。class.forName在load class之后,执行了初始化的工作,即static变量和static块的工作。 而loadclass没有。 如果你仅仅是执行一个类的static块,而不想干别的。那么可以用class.forName, 比如jdbc驱动的注册。

12.newInstance和new的区别, newInstance只能构造无参的对象。 都是loader之后,初始化,然后实例化的过程。

13,一个classloade进来后拥有唯一表示ClassLoader#类的全包名。classLoader会记录真正装载自己的那个loader。事实上loadeClass方法第一步都会有个从cache里get的过程。
ClassLoaderName#类的全包名是天然的键值。再回头看问题10,根据类名得到加载他的classLoader的具体型号,是可以实现的。

14 web容器的classloader组成,Bootstrap ClassLoader、Extension ClassLoader肯定是少不了。 app Classloader树里重要的有loader 有Common(负责加载serlet-api等) Shared(可以把所有项目公用的放在里边) webapp1  webapp2 ..(自己独特的)

15 tomcat的类加载 打破了类的双亲委派模式。先找自己。
比如在工程目录的web-inf/lib 目录下放一个 servlet-api 会发生什么?访问会报错。
因为从容器--项目业务方法--容器这个过程就是通过servlet-api里边的request和response  link上关系的。
现在CommonClassLoader#javax.servlet.http.HttpServletRequest 和 WebappClassLoader#javax.servlet.http.HttpServletRequest会被jvm认为是两个不同的类。
可以想象,上文讲的class.forName 会有点问题,因为#后面的类名查class loader的型号,可能查出多个来。class只有在用到的时候才会去load,绝不是上来就一股脑load。然后放在缓存里。


16 其他的web容器比如jetty是标准实现,比如在工程目录里放一个 servlet-api会如何? 不会有任何问题,子类的classLoader loader的时候,会调用Common ClassLoader的loader方法,然后发现CommonClassLoader#javax.servlet.http.HttpServletRequest已经存在。就直接返回了,也就是说工程目录下的servelt-api会被无视掉。


17 如果一个classPath里下有2个一样的jar? 先入为主的原则,所以版本如果有差异就会出问题。依赖于高版本需要的某个方法,在低版本里没有。


18 reload。 第一步:classloader的classpath需要更新。实际上urlClassLoader就有动态添加classPath的方法addURL。第二步,把中间过程干掉,比如缓存,load class的过程必须要重新来,因为.class文件可能会被改。

19. 一般web 服务都是大并发。直接kill 进程再start的方式并不友好。所以都要提供平滑重载机制reload。 掌握了class loader的机制,写一个reload的过程并不难。 像web容器的热重启机制,就是起一个单独的线程,去监控classPath下所有改动需重启的文件比如.class文件,配置文件或者jar文件,比对他们的文件修改时间。一旦有改动,马上reload。 所以算是比较耗费性能的一件事。

20. 其实比较粗暴一点的reload方式,是干掉classLoader重写创建一个。这样带来的缺点是由于上一个classLoader被干掉,通过他load进来的class(对他有引用和被引用的关系)的引用链就断了,产生一堆垃圾对象。 不过作者观察过,所有被前一个废弃的classLoaderload进来的class会被垃圾收集机制正常的回收。



猜你喜欢

转载自supben.iteye.com/blog/1724245