`
liaoyixun
  • 浏览: 47080 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

ClassLoader类装载策略

阅读更多
Java编程极限考验:ClassLoader类装载策略
作者:banq 发表时间:2004年07月26日 15:11 回复


原贴网址: http://www.jdon.com/jivejdon/thread/15456.html

个人认为,Java编程中极限考验是Classloader机制的掌握和灵活运用,特别是在复杂的系统,如存在动态类装载,Reflect,EJB,AOP等环境。

CLass.forName()
和Thread.currentThread().getContextClassLoader())
是否一样?

在很多文章中,都认为两者是一致的,如Java研究组织中一篇文章,被我从google搜索到的:
http://www.javaresearch.org/article/showarticle.jsp?column=31&thread=10178
文中说"这个方法可以用Class.forName()代替",在一般简单情况是可以替代,但实际上有时候是不能替代的。

Classloader存在下面问题:
在一个JVM中可能存在多个ClassLoader,每个ClassLoader拥有自己的NameSpace。一个ClassLoader只能拥有一个class对象类型的实例,但是不同的ClassLoader可能拥有相同的class对象实例,这时可能产生致命的问题。如ClassLoaderA,装载了类A的类型实例A1,而ClassLoaderB,也装载了类A的对象实例A2。逻辑上讲A1=A2,但是由于A1和A2来自于不同的ClassLoader,它们实际上是完全不同的,如果A中定义了一个静态变量c,则c在不同的ClassLoader中的值是不同的。

因此,研究JBoss的ClassLoader策略,对于更好地实现EJB组件拼装是用好处的,因为,一个项目中可能要用其他项目的EJB组件,如何实现运行时EJB组件共享,如何实现EJB组件打包是很重要的。


为了说明ClassLoader对于复杂架构是至重关键,列举开源Portal产品Exo中ServivesManager类内容。

该类是Exo利用PicoCOntainer实现功能性Service JavaBeans初始化,在将那些Service性质的JavaBeans加载到pico中时,需要使用到Classloader,
Exo专门设立一个ServiceContext类:




public class ServiceContext {  private ClassLoader cl;  //包含Classloader信息  private Services services;  public ServiceContext(ClassLoader cl, Services services) {    this.cl = cl;    this.services = services;  }  public ClassLoader getCl() {    return cl;  }  public Services getServices() {    return services;  }}


在ServicesManager中,有:
private ClassLoader updatedClassLoader;

它的初始值是:
Thread.currentThread().getContextClassLoader();

如果,这里写Class.forName 那么简单,那么你头疼去吧。

但是这样不够:
在addService方法中,根据加入的不同ServiceContext实现类装载:



public void addService(ServiceContext context) {    Services servicesToAdd = context.getServices();    String name = servicesToAdd.getName();    URLClassLoader cl = null;    if(context.getCl() instanceof URLClassLoader) {      cl = (URLClassLoader) context.getCl();    } else {      cl = URLClassLoader.newInstance(new URL[]{}, context.getCl());    }    updatedClassLoader = new URLClassLoader(cl.getURLs(), updatedClassLoader);    synchronized (servicesContext) {      servicesContext.put(name, context);      reloadContainer();    }  }


其实向Picocontainer中加入一个服务很简单,上述方法的主要代码是处理Classloader,考虑到Classloader有嵌套关系,上述代码小心使用这个Service服务的父Classloader,使用父Classloader装载服务Service。


希望有兴趣者一起讨论。




Re: Java编程极限考验:ClassLoader类装载策略  发表: 2004年07月26日 15:35  回复 

banq 发表文章: 8564/ 注册时间: 2002年08月03日 17:08 

下面一段代码 是在Web容器中,实现Servlet类或Jsp类的动态装载,一般Servlet类部署到Web容器,是由Web容器监测Web.xml是否变化,<br>以决定是否装载,这是非常被动的,使用下面代码通过ClassLoader可以在自己程序中主动实现装载:<br>


public class WrapperServlet extends Servlet {   public void init(ServletConfig config) {     m_config = config; // stored as you don't want this to change   }   public void doGet(...) throws ServletException {     getServlet().doGet(...);   }   private Servlet getServlet() throws ServletException {     if ((m_servlet == null) || haveClassesChanged()) {         创造一个独立的classloader 确保它的父是我们的自己的ClassLoader<br>        path_to_separate_classes是将被装载的路       URLClassLoader loader = new URLClassLoader(new URL[] {         new URL("path_to_separate_classes");       }, WrapperServlet.class.getClassLoader());       从被装载的路径使用自己装载器来装载Servlet类              Class servletClass =         loader.loadClass(m_config.getInitParameter("servletClass");       m_servlet = (Servlet)servletClass.newInstance();       m_servlet.init(m_config);     }     return m_servlet;   }   private boolean haveClassesChanged() {     // determine whether classes have changed somehow   }   private ServletConfig m_config;   private Servlet m_servlet = null;}





Re: Java编程极限考验:ClassLoader类装载策略  发表: 2004年07月27日 10:13  回复 

flyisland 发表文章: 15/ 注册时间: 2004年04月22日 13:33 

以前写的一篇文章:http://www.blogbus.com/blogbus/blog/diary.php?diaryid=153255,可供参考。



Re: Java编程极限考验:ClassLoader类装载策略  发表: 2004年07月27日 10:32  回复 

cats_tiger 发表文章: 178/ 注册时间: 2003年05月16日 16:58 

嗯,难道需要编写通用的ClassLoader查询类?我写了一个...


// 摘自javax.xml.parsers.FactoryFinderpublic final class ClassLoaderFinder {  private ClassLoaderFinder() {  }  /**   * Figure out which ClassLoader to use.  For JDK 1.2 and later use the   * context ClassLoader if possible.  Note: we defer linking the class   * that calls an API only in JDK 1.2 until runtime so that we can catch   * LinkageError so that this code will run in older non-Sun JVMs such   * as the Microsoft JVM in IE.   */  public static ClassLoader findClassLoader() throws ConfigurationError {    ClassLoader classLoader;    try {      // Construct the name of the concrete class to instantiate      Class clazz = Class.forName(ClassLoaderFinder.class.getName()                                  + "$ClassLoaderFinderConcrete");      ContextClassLoaderFinder clf = (ContextClassLoaderFinder) clazz.          newInstance();      classLoader = clf.getContextClassLoader();    }    catch (LinkageError le) {      // Assume that we are running JDK 1.1, use the current ClassLoader      classLoader = ClassLoaderFinder.class.getClassLoader();    }    catch (ClassNotFoundException x) {      // This case should not normally happen.  MS IE can throw this      // instead of a LinkageError the second time Class.forName() is      // called so assume that we are running JDK 1.1 and use the      // current ClassLoader      classLoader = ClassLoaderFinder.class.getClassLoader();    }    catch (Exception x) {      // Something abnormal happened so throw an error      throw new ConfigurationError(x.toString(), x);    }    return classLoader;  }  static class ConfigurationError      extends Error {    private Exception exception;    /**     * Construct a new instance with the specified detail string and     * exception.     */    ConfigurationError(String msg, Exception x) {      super(msg);      this.exception = x;    }    Exception getException() {      return exception;    }  }  /*   * The following nested classes allow getContextClassLoader() to be   * called only on JDK 1.2 and yet run in older JDK 1.1 JVMs   */  private static abstract class ContextClassLoaderFinder {    abstract ClassLoader getContextClassLoader();  }  static class ClassLoaderFinderConcrete      extends ContextClassLoaderFinder {    ClassLoader getContextClassLoader() {      return Thread.currentThread().getContextClassLoader();    }  }  //Sample  public static void main(String args[]) {    try {      final String CLASS_NAME = "oracle.jdbc.driver.OracleDriver";      Class spiClass;      ClassLoader classLoader = ClassLoaderFinder.findClassLoader();      if (classLoader == null) {        System.out.println("From Class.forName");        spiClass = Class.forName(CLASS_NAME);      }      else {        System.out.println("From ClassLoaderFinder");        spiClass = classLoader.loadClass(CLASS_NAME);        spiClass.newInstance();      }      java.sql.Connection conn = java.sql.DriverManager.getConnection(          "jdbc:oracle:thin:@localhost:1521:ORCL",          "scott",          "tiger");      java.sql.Statement stmt = conn.createStatement();      java.sql.ResultSet rs = stmt.executeQuery("select * from cat");      while (rs.next()) {        System.out.println(rs.getString(2));      }    }    catch (Exception e) {      e.printStackTrace();    }  }}




Re: Java编程极限考验:ClassLoader类装载策略  发表: 2004年07月27日 10:53  回复 

asdlcj 发表文章: 22/ 注册时间: 2004年03月19日 23:42 

大家还是看深入java虚拟机那本书把

说的很详细了

我不想再多说了!



Re: Java编程极限考验:ClassLoader类装载策略  发表: 2004年07月27日 11:06  回复 

banq 发表文章: 8564/ 注册时间: 2002年08月03日 17:08 

深入java虚拟机只是从原理机制谈了ClassLoader,关键是应用,面对复杂环境的应用,比如别人的框架已经有ClassLoader,你在别人的框架下还要ClassLoader,你就得注意父子关系,但是你也可以建立并行关系。

在你决定如何建立你的CLassLoader策略时,你必须对你基于的框架内部有了解,因为你的应用和JVM之间有很多层,有EJB容器层 有你的框架层,你也必须了解这中间层的ClassLoader策略,只是掌握JVM还不够的。

简单地说,如果你基于AOP框架或EJB容器实现ClassLoader,那么你必须对你的AOP框架或EJB容器的ClassLoader策略掌握,Weblogic和JBoss 3.2机制就不一样,各个服务器都不同,不同版本不同,如果你不注意这些变化,那么你的应用系统迁移就有问题,甚至无法实现迁移。





Re: Java编程极限考验:ClassLoader类装载策略  发表: 2004年07月27日 11:08  回复 

banq 发表文章: 8564/ 注册时间: 2002年08月03日 17:08 

to cats_tiger
你做了findClassLoader,但是没有构造自己的ClassLoader,还是有漏洞,你的Classloader是使用Class.forName,这个问题在复杂环境下会有问题,特别是动态代理Proxy下不正常,有时会报ClassCastException错误。这也是很多人对动态代理感到无法掌握的原因之一。




Re: Java编程极限考验:ClassLoader类装载策略  发表: 2004年07月27日 12:14  回复 

cats_tiger 发表文章: 178/ 注册时间: 2003年05月16日 16:58 

to banq
难道要这样?好像太麻烦了。

package cloader;public final class ClassLoaderFinder {  private ClassLoaderFinder() {  }    /**   * The following nested classes extends the <tt>ClassLoader</tt> so that we   * can create our ClassLoader freely.   */  private static class PersonalClassLoader extends ClassLoader {    public PersonalClassLoader(ClassLoader parentLoader) {      super(parentLoader);    }        public PersonalClassLoader() {      //Use the external class's <tt>ClassLoader</tt>      super(ClassLoaderFinder.class.getClassLoader());    }  }  /**   * Figure out which ClassLoader to use.  For JDK 1.2 and later use the   * context ClassLoader if possible.  Note: we defer linking the class   * that calls an API only in JDK 1.2 until runtime so that we can catch   * LinkageError so that this code will run in older non-Sun JVMs such   * as the Microsoft JVM in IE.   */  public static ClassLoader findClassLoader() throws ConfigurationError {    ClassLoader classLoader;    try {      // Construct the name of the concrete class to instantiate      PersonalClassLoader pLoader = new PersonalClassLoader();      Class clazz = pLoader.loadClass(ClassLoaderFinder.class.getName()                                         + "$ClassLoaderFinderConcrete");      //Class clazz = Class.forName(ClassLoaderFinder.class.getName()      //                            + "$ClassLoaderFinderConcrete");      ContextClassLoaderFinder clf = (ContextClassLoaderFinder) clazz.          newInstance();      classLoader = clf.getContextClassLoader();    }    catch (LinkageError le) {      // Assume that we are running JDK 1.1, use the current ClassLoader      classLoader = ClassLoaderFinder.class.getClassLoader();    }    catch (ClassNotFoundException x) {      // This case should not normally happen.  MS IE can throw this      // instead of a LinkageError the second time Class.forName() is      // called so assume that we are running JDK 1.1 and use the      // current ClassLoader      classLoader = ClassLoaderFinder.class.getClassLoader();    }    catch (Exception x) {      // Something abnormal happened so throw an error      throw new ConfigurationError(x.toString(), x);    }    return classLoader;  }  static class ConfigurationError      extends Error {    private Exception exception;    /**     * Construct a new instance with the specified detail string and     * exception.     */    ConfigurationError(String msg, Exception x) {      super(msg);      this.exception = x;    }    Exception getException() {      return exception;    }  }  /*   * The following nested classes allow getContextClassLoader() to be   * called only on JDK 1.2 and yet run in older JDK 1.1 JVMs   */  private static abstract class ContextClassLoaderFinder {    abstract ClassLoader getContextClassLoader();  }  static class ClassLoaderFinderConcrete      extends ContextClassLoaderFinder {    ClassLoader getContextClassLoader() {      return Thread.currentThread().getContextClassLoader();    }  }  //Sample  }





Re: Java编程极限考验:ClassLoader类装载策略  发表: 2004年07月27日 12:20  回复 

cats_tiger 发表文章: 178/ 注册时间: 2003年05月16日 16:58 

实在是麻烦,绕来绕去的。
看来怎样处理ClassLoader,要根据具体的应用具体分析,从运行环境(Container)、用途等方面综合考虑。
绝大多数应用系统Class.forName就足够了。



Re: Java编程极限考验:ClassLoader类装载策略  发表: 2004年07月27日 12:35  回复 

banq 发表文章: 8564/ 注册时间: 2002年08月03日 17:08 

>绝大多数应用系统Class.forName就足够了
这个定义如果存在的话,Exo的ServiceManager就不必这么复杂,Exo作为一个设计概念非常前卫的开源系统,在ServiceManager将ClassLoader搞得很复杂是有道理的,是经验的总结,使得自己的系统伸缩性强,健壮性更强,这些都是严格要求的设计目标所致。

这里我想讨论的是,不怕复杂,解剖其本质。

另外,自己定义ClassLoader象上面的代码是可以,但是在具体情况时还要注意,可能需要将原来的ClassLoader机制暂存,用自己定义的ClassLoader,然后再恢复原来的ClassLoader。





Re: Java编程极限考验:ClassLoader类装载策略  发表: 2004年07月27日 16:06  回复 

kewan 发表文章: 46/ 注册时间: 2003年04月20日 19:11 

ClassLoader其实是很简单的,以树型来组织。我看是楼上的各位把把简单的问题搞复杂了。



Re: Java编程极限考验:ClassLoader类装载策略  发表: 2004年07月28日 00:41  回复 

cats_tiger 发表文章: 178/ 注册时间: 2003年05月16日 16:58 

> ClassLoader其实是很简单的,以树型来组织。我看是楼上的?> 位把把简单的问题搞复杂了。

这个帖子的目的主要是研究一下ClassLoader的应用,而不是它的原理,所以复杂一些(很多原理简单的东西,用起来却复杂,例如围棋)。
但是,如果你的应用可能会在不同的JVM(例如MS JVM)下或不同的Container中运行,就要考虑ClassLoader的问题了。比如基于xml的静态工厂类,通常用Class.forName就行,但是在有些情况下却出现异常。




Re: Java编程极限考验:ClassLoader类装载策略  发表: 2004年07月28日 08:53  回复 

kewan 发表文章: 46/ 注册时间: 2003年04月20日 19:11 

> 这个帖子的目的主要是研究一下ClassLoader的应用,而不是?> 的原理,所以复杂一些(很多原理简单的东西,用起来却复杂
> 缥澹?> 但是,如果你的应用可能会在不同的JVM(例如MS
> JVM)下或不同的Container中运行,就要考虑ClassLoader的?> 题了。比如基于xml的静态工厂类,通常用Class.forName就行
> 窃谟行┣榭鱿氯闯鱿忠斐!?

不知道你说的基于xml的静态工厂类是怎么回事。
装入类和是否通过Class.forName没有必然的联系。
任何一个类装入都有一个运行环境和边界,搞清楚这个就行,没那么复杂。类要在该装入的地方装入。每个ClassLoader指的就是一个运行环境,每个ClassLoader也都有一个边界,类必须在这个边界内运行。
如果在不同的JVM下运行(不管是Sun的JVM还是MS的JVM,于此无关),应该和ClassLoader没有关系。比如一个分布式的Web应用,类的装入都在本地,和其他的JVM无关。如果考虑集群的问题,就不是ClassLoader的问题了。这一块没有做过,请各位指正。



Re: Java编程极限考验:ClassLoader类装载策略  发表: 2004年07月28日 09:39  回复 

cats_tiger 发表文章: 178/ 注册时间: 2003年05月16日 16:58 

>不知道你说的基于xml的静态工厂类是怎么回事。
>装入类和是否通过Class.forName没有必然的联系。
就是读取xml中定义的类名,然后实例化,返回父类型的那种工厂。
>任何一个类装入都有一个运行环境和边界,搞清楚这个就行,没那么复杂。
问题是有时候没有那么容易搞清楚。建议参考javax.xml.parsers.DocumentBuilderFactory的源代码



Re: Java编程极限考验:ClassLoader类装载策略  发表: 2004年07月28日 15:45  回复 

kewan 发表文章: 46/ 注册时间: 2003年04月20日 19:11 

那就先搞清楚再装入,呵呵。没有必要纠缠在ClassLoader上,把ClassLoader搞复杂了,并没有任何的好处。
另外,parser并没有你说得那么复杂。哈哈



  

这个主题共有 49 回复 / 4 页 [ 1 2 3 4 下一页 ]   

   上一篇: 如何处理对象删除时引用的判断  下一篇: 对于工商局中的线索管理业务如何画出四色模.. 
   

  查询本论坛 近一天 近三天 近一周 近一月 近三月 近半年 近一年 所有 最热门帖子  


   快速发表回复: 标题 
                       

内容   
分享到:
评论
1 楼 pouyang 2011-09-01  
很感谢您的文章,但是能不能排版一下?

相关推荐

    java ClassLoader机制及其在OSGi中的应用

    三、ClassLoader的装载策略 从Java 1.2版本开始,引入了双亲委派模型(Delegation Model)。在该模型下,当一个ClassLoader收到加载类的请求时,它首先会委托父加载器去尝试加载,只有当父加载器无法加载时,当前...

    深入JVM内核—原理、诊断与优化视频教程-6. 类装载器

    过度的类装载可能导致方法区溢出,因此优化类装载策略对内存管理至关重要。 通过深入理解和掌握这些知识点,开发者能够更好地调试和优化Java应用程序,尤其是在大型分布式系统中,正确管理类装载器对于系统稳定性和...

    Java虚拟机类装载的原理及实现

    - **安全性增强**:通过自定义类装载器实施更严格的类验证或安全策略。 #### 实现动态装载的关键方法 实现动态装载的关键在于理解并利用ClassLoader中的几个重要方法: - `loadClass(String name, boolean ...

    [浅析J2EE应用服务器的JAVA类装载器]python回朔异常的模块.docx

    开发者可以通过自定义类装载器来满足特定的应用场景,例如加载特定版本的库或实现特定的加载策略。 总结来说,Java类装载器机制是Java平台的核心特性之一,它确保了代码的动态加载和隔离,使得J2EE应用服务器能够...

    ClassLoader in OSGI

    #### 三、ClassLoader装载策略 从Java 1.2版本开始,引入了双亲委托模型来确保Java平台的安全性。在这种模型下,当一个装载器被请求装载某个类时,它首先会委托给自己的父装载器去装载,如果父装载器能够装载,则...

    websphere6,7上修改类装载顺序为应用程序优先

    "websphere6,7上修改类装载顺序为应用程序优先"这个话题涉及到如何调整WebSphere的类加载策略,以解决可能存在的类版本冲突问题。 WebSphere应用服务器默认的类加载顺序是先从系统类加载器(System ClassLoader)...

    java四大核心思想.pdf

    2. **类装载器**:Java应用程序中的类装载器分为两类:启动类装载器(Bootstrap ClassLoader)和用户定义的类装载器。启动类装载器负责加载Java API和其他基础类,而用户定义的类装载器可按需加载自定义类,如从网络...

    深入Java虚拟机

    类装载器分为启动类装载器(Bootstrap ClassLoader)、扩展类装载器(Extension ClassLoader)和应用程序类装载器(Application ClassLoader)。它们遵循双亲委托模型,即先由父类装载器尝试加载,如果失败再由子类...

    深入Java虚拟机读书笔记1

    总的来说,Java虚拟机通过其复杂的架构实现了跨平台的运行能力,提供了高效的代码执行机制,以及灵活的类装载和内存管理策略,使得Java应用程序能够在各种环境中高效、稳定地运行。深入学习JVM有助于我们更好地优化...

    深入JVM内核—原理、诊断与优化视频教程-8.Java堆分析.

    JVM有三个层次的类装载器:引导类装载器(Bootstrap ClassLoader)、扩展类装载器(Extension ClassLoader)和应用类装载器(Application ClassLoader)。用户还可以自定义类装载器。 通过学习本教程,你将掌握如何...

    Java四大核心技术思想详解.doc

    Java中存在两类类装载器:启动类装载器(Bootstrap ClassLoader)和用户自定义类装载器。启动类装载器负责加载Java API类,而用户自定义类装载器允许在运行时动态加载类,这为应用程序提供了可扩展性和灵活性。类...

    基于沙箱的Java安全体系结构

    #### 类装载器(ClassLoader) 类装载器是Java沙箱安全模型的第一道防线,它的主要职责是将编译好的类文件加载到JVM中执行。类装载器的设计考虑到了多个层面的安全性问题: - **命名空间与运行时包**:类装载器为每...

    JVM的相关概念.doc

    - **用户自定义类装载器**:用户可以通过继承`java.lang.ClassLoader`类来自定义类装载器,用于加载CLASSPATH目录下的类。 **2.2 执行引擎** 执行引擎是JVM的核心组件之一,负责执行字节码。字节码是一种中间代码...

    Java虚拟机装载和初始化一个class类代码解析

    在 Java 中,类的装载策略是由类加载器控制的。类加载器会在必须加载类时,才开始装载类。如果已装载完毕的类引用了某些未装载的类,类装载器会进一步去装载这些类,整个的装载过程是递归的。 类加载器 Java 中...

    jvm_code-master.rar

    1. 类装载器(ClassLoader):负责加载类文件,将字节码转化为内存中的Class对象。类装载器分为引导类装载器、扩展类装载器和应用程序类装载器,以及用户自定义的类装载器。 2. 运行时数据区(Runtime Data Area)...

    JBoss JMX实现架构

    当需要加载类时,先由父类加载器尝试,如果找不到则递归到更高级别的父类加载器,直到到达根类加载器(Bootstrap ClassLoader)。这种机制确保了类型安全,防止了不同版本的类冲突。 2.2.2 类装载和Java中的类型 ...

    classworlds

    "Classworlds"是一个专为Java开发者设计的框架,它简化了类加载器(ClassLoader)的操作,解决了Java中与类装载相关的一些复杂性和困扰。在Java中,类加载是程序运行的关键部分,因为它负责找到并加载类到JVM(Java...

    面试必问之jvm与性能优化.docx

    - **Bootstrap ClassLoader(启动类加载器)**:这是最顶层的类加载器,由JVM自身实现(通常使用C++编写),用于加载Java的核心库,如`rt.jar`。它没有父加载器。 - **Extension ClassLoader(扩展类加载器)**:由`sun....

Global site tag (gtag.js) - Google Analytics