WebSphere应用服务器类加载机制实践

时间:2021-02-06 19:38:39
【IT168 专稿】    本文将会从Java平台本身的类加载机制谈起,然后向读者展现WebSphere Application Server(WAS)中如何应用扩展Java本身的类加载机制。本文还将给出一些常见的由类加载的造成的问题,分析背后的原因,并给出相应的解决方案。

    关键字:

   JVM— Java Virtual Machine,Java虚拟机,是运行Java程序的平台,不同的操作系统有不同的实现。

    classloader — 类加载器,是JVM的一个重要组件,完成加载Java Class文件进JVM。

    Class — Java类型定义文件,定义的数据与相应操作方法,可被classloader加载到JVM上根据它产生相应的运行实例,并可以调用相应的方法。

    让我们从最基本的说起 — Java平台的类加载机制

    首先大家要知道为什么要有classloader,它是做什么的?

    Java是一门语言,但它更一个平台,因为它提供了一个运行平台JVM(虚拟机),隔离了将要运行的程序与当前所在的操作系统。

    而在Java这个平台要运行什么本身是动态未知的,我们写的Java程序都是在需要的时候被动态的加载上去的。Java本身提供了一套类加载机器。总的来说,所有装在Java虚拟机上运行的类都是由classloader这个东西来完成的。

    那classloader是如何工作的?

    首先要强调一点,所有的Java类都是由classloader动态的加载到Java虚拟机上然后再动行的。这里的动态主要表现方式是三种,见表1。

WebSphere应用服务器类加载机制实践

表1 Java的动态加载特性

    当JVM(Java虚拟机)默认启动时,会形成由三个类加载器组成的初始类加载器层次结构:

    bootstrap classloader

    |

    extension classloader

    |

    system classloader

    bootstrap classloader — 引导(也称为原始)类加载器,它负责加载Java的核心类。

    extension classloader — 扩展类加载器,它负责加载JRE的扩展目录(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系统属性指定的)中JAR的类包。

    system classloader — 系统(也称为应用)类加载器,它负责在JVM被启动时,加载来自在命令java中的-classpath或者java.class.path系统属性或者CLASSPATH操作系统属性所指定的JAR类包和类路径。

    如上所示,classloader是有父子结构的,如system classloader的父亲是extension classloader,extension classloader的父亲bootstrap classloader,bootstrap classloader没有父亲,它是JVM一启动就存在的。

    我们还可以扩展已有的Classloader类,通过覆盖ClassLoader的findClass方法来实现自己的载入策略,甚至覆盖loadClass方法来实现自己的载入过程,这样来满足加载特定类的需求(比如只加载名字前缀是ABC的类)。这样它就成了system classloader下面的第四层。当然,因为这个我们扩展的classloader本身也是一个Java类,它也是被前面的某个classloader加载进来的.

    在默认的JVM设计中,classloader加载Class的过程大致如下:

    1.检测此Class是否载入过(即在JVM中是否有此Class),如果有到第8,如果没有到第2。

    2.如果父亲classloader不存在(没有父亲,那一定是bootstrap classloader了),到第4。

    3.请求父亲classloader载入,如果成功到第8,不成功到第5。

    4.请求JVM让bootstrap classloader去加载,如果成功到第8。

    5.寻找Class文件(从与此classloader相关的类路径中寻找)。如果找不到则到第7。

    6.从文件中载入Class,到第8。

    7.抛出ClassNotFoundException。

    8.返回Class。

    我们可以看到,classloader加载Class的顺序是先委托其parent来加载,直到所有的parent都不能加载才自己去加载。

    Java使用classloader原因,除了可以达到动态性之外,其实最重要的原因还有安全性,体现在下面两点:

    1.核心的类不可能被仿照(这主要是因为先parent委托机制的作用),比如我们不可能加载一个自己写的放在classpath下的java.lang.String类,因为Java总是parent优先,在系统目录下面的String类总是被优先加载。

    2.我们可以指定,控制到特定的载入点,不会误用,比如我只要加载目录的ABC下面的类。

    Java中每个类别都是与加载其进虚拟机的classloader相关联起来的,在同一个运行时的虚拟机中,同一个类型可以被不同的classloader载入多次。

 

WAS中的类加载机制

    了解完Java本身提供的类加载机制后,下面我们看一下WebSphere Application Server(WAS)是如何利用并扩展JVM默认的已有的类加载框架。

    如下是WAS的classloader层次,这里的每一层次可能有多个有父子关系的classloader组成,只是它们的目的是相同或者相关的,所以放在同一层,如表2所示。

WebSphere应用服务器类加载机制实践

 

表2 WAS Classloader层次

    下面介绍每个层次的classloader所能完成的事,以从顶至下的顺序。注意,区别于JVM默认classloader加载类策略的父亲优先(PARENT_FIRST)的加载策略,WAS中有些扩展的classloader层次可以定义自己的委托策略,比如是父亲优先(PARENT_FIRST)或者父亲最后(PARENT_LAST)。

    1) JVM Classloader层

    这个层其实就是前面介绍的Java基本的classloader框架,由bootstrap classloader、extension classloader、system classloader三个classloader组成,主要是完成jre/lib下,java.ext.dir下,还有JVM classpath下面类的加载。

    2) WebSphere Extensions Classloader层

    这个层的classloader主要完成WAS运行时所需要的类以及相关资源,比如JDBC,Resource Adapter,以及用户扩展WAS运行时库的目录,这一层的委托策略都是父亲优先(PARENT_FIRST)。

    3) WebSphere lib/app Class loader

    这个层的classloader主要是为了兼容WAS Version 4的。它主要用来加载一些所有应用都共享的类,在Version 4以后,推荐用Shared libraries来放需要共享的类库,所以这里就不详细介绍它了。

    4) WebSphere "server" Class loader

    这层的classloader主要是来加载整个运行上当前服务器上的应用程序所共享的的类库,你可以通过定义特定的Shared libraries以来使当前的服务器中的应用程序共享它,这一层的委托策略是父亲优先(PARENT_FIRST)。

    5) Application Class loader

    下面我们到了WASclassloader的最底层。这一层由两个classloader组成:

    第一个是Application Module Class loader,主要负责加载应用程序单元。对于应用程序中的Web单元,我们还可以设定用一个Web Module Class loader去加载(这时它的父亲就是Application Module Class loader),它是WAS classloader中的最底层。

    对于这两个classloader,可以采取的委托策略可以既是父亲优先(PARENT_FIRST),也可以父亲最后(PARENT_LAST)。

    同时,我们还可以指点是单个Application Module Class loader去加载所有的应用程序或者每个应用程序都有自己的Application Module Class loader,这是也为更好的提高classloader的隔离性(Isolation),其选项分别为Multiple与Single。

 

为了给大家一个直观的感觉,下面给出一个例子说明一个实际项目中classloader的使用情况。

    如下图所示,在WAS上有两个应用程序:Application 1与Applicatoin 2。这个WAS的Application Module Class loader策略被指定成Multiple,这样每个应用程序都会有一个classloader。同时Application 1的Web Module类加载策略被设成“Module”,这样它就会有一个相应的Web Module Class loader来加载相应的web单元。相反,Application 2的Web Module类加载策略被设成“Application”,这样加载Application 2的classloader还会加载它的web单元,见图1。

WebSphere应用服务器类加载机制实践

 

图1 Application 1与Applicatoin 2的classloader结构

    WAS中classloader的最佳实践与常见问题分析解决

    下面是一些使用WAS classloader的最佳实践:

    1) 不要把应用程序依赖的资源放在JVM的系统lib下让JVM的classloader去加载,原因是如果应用程序的委托策略设成PARENT_LAST,可能会造ClassNotFoundException。

    2) 不要修改ws.ext.dirs这个系统属性以此来增加WebSphere Extensions Classloader的加载能力,原因是被扩展的目录可能跟当前已经目录中有冲突,会导致一此不可预期的问题。

    3) 如开启了动态重加载,这种情况下应用如果有对本地类库的依赖关系,最好使用WebSphere "server" Class loader来加载,这样服务器的整个生命周期这些库都能被应用程序可见,即使应用程序被重新加载。

    下面再给出两个实际WAS classloader的相关问题的分析与相应解决方案。

    1) 如果在启动程序时出现如下异常:

    · ClassCastException

    · ClassNotFoundException

    · NoClassDefFoundException

    · UnsatisfiedLinkError

    主要原因是未正确配置负责装入应用程序各单元的类装入器,可以查看一下当前应用程序的类加载结构。一般,我们可以从SystemOut.log这个log文件中找出当前的类加载路径设置,其中包括要加载的库以及还有WAS的扩展目录等。我们还可以利用WAS提供的Class Loader Viewer来更直接的看当前应用程序的类加载情况,方法如下:

    单击Troubleshooting->Class Loader Viewer,进去后可以看到当前服务器上所有的应用程序,然后选取你关注的,然后会列出这个应用的所有组成单元,点击出现异常的那个单元,就会出现这个单元的classloader结构图,依次可以看到从顶到底的classloader结构图,并可以看到所有可以被加载的类或者类库。这样,可以分析当前出现的异常是不是因为不在这个单元的可加载视图里面,或者是不是因为跟别的当前能被加载的类有冲突。图2是一个查看实际应用程序的例子。

 WebSphere应用服务器类加载机制实践

 

图2 查看应用程序的类加载情况

    2) 为何更新了应用的Resource Adapter,但不起作用?

    经常会有这样的情况,假设我们的应用原来用某个版本的Resource Adapter去联连后面的业务系统,因为有了新版本的Resource Adapter,我们更新了应用中的Resource Adapter,然后重新build后发布到服务器上。但是发现这个新版本的某个功能没有起作用。这里可能的原因是当前的服务器上你曾经有过以standalone的方式(让所有应用程序共享)安装了旧版本的Resource Adapter,如图3所示如何查看当前服务器上安装的standalone Resource Adapter。

WebSphere应用服务器类加载机制实践

 

图3 查看WAS已经安装的standalone Resource Adapter

    如果以Standalone方式安装了旧版本的话,即使你的应用程序中包括了最新的Resource Adapter,但它也不会起作用,原因是应用的类加载默认策略是父亲优先(PARENT_FIRST),它会让WebSphere Extensions Classloader先去试着加载Resource Adapter,而WebSphere Extensions Classloader会把standalone安装的Resource Adapter加载进内存来给应用程序使用。解决方法是更新这个standalone Resource Adapter或者卸载它。

    读者可以学习参考资料部分的资料来进一步提高对WAS classloader的了解。

    参考资料:

    Inside The Java Virtual Machine by  Bill Vennershttp://www.amazon.com/Inside-Java-Virtual-Machine-Venners/dp/0071350934

    IBM RedBook : WebSphere Application Server V6.1 : System Management and Configurationhttp://www.redbooks.ibm.com/abstracts/sg247304.html?Open

    TroubleShooting: Classloader problems for WebSphere Application Server http://www-01.ibm.com/support/docview.wss?rs=180&uid=swg21219358