### Introduction

One of the main concerns of a developer writing hot re-deployable JBoss applications is to understand how JBoss class loading works. Within the internals of the class loading mechanism lies the answer to questions like

• What happens if I pack a newer version of an utility library with my application, while an older version of the same library lingers somewhere in the server's lib directory?
• How can I use two different versions of the same utility library, simultaneously, within the same instance of the application server?
• What version of an utility class I am currently using?

or even the most fundamental of them all

• Why do I need to mess with all this class loading stuff anyway?

This article tries to provide the reader with the knowledge required to answer these questions. It will start by trying to answer the last one, and then it will present several often encountered use cases and explain the behavior of the JBoss class loading mechanism when faced with those situations.

#### Class Namespace Isolation

An application server should ideally give its deployed applications the freedom to use whatever utility library and whatever version of the library they see fit, regardless of the presence of concurrent applications that want to use the same library. This is mandated by the J2EE specifications, which calls it class namespace isolation. The fact that different applications load their classes in different class name spaces or class loading domains allow them to do just that: run whatever class version they like, oblivious to the fact that their neighbors use the same class.

Java doesn't provide the formal notion of class version. So how is it possible to implement a class loading domain? The runtime identity of a class in Java 2 is defined by the fully qualified class name and its defining class loader. This means that the same class, loaded by two different class loaders, is seen by the Virtual Machine as two completely different types.

#### Hot Redeployment

Returning to application servers, the need for class loaders becomes probably obvious: this is how an application server implements class namespace isolation: each application gets its own class loader at deployment, and hence, its own "version" of classes.

To extend this even more, it would be nice if we could re-deploy an application (i.e. instantiate a newer version of a class), at run-time, without necessarily bringing down the VM and re-starting it to reload the class. That would mean 24x7 uptime. Java 4 doesn't intrinsically support the concept of hot re-deployment. Once a dependent class reference was added to the runtime constant pool of a class, it is not possible to drop that reference anymore.

However, it is possible to trick the server (or the VM) into doing this. If application A interacts with application B, but doesn't have any direct references to the B classes, and B changes, let's say a newer and better version becomes available, it is possible to create a new class loader, load the new B classes and have the invocation bus (the application server) route all invocations from A to the new B classes. This way, A deals with a new version of B without even knowing it. If the application server is careful to drop all explicit references to the old B classes, they will eventually be garbage collected and the old B will eventually disappear from the system.

#### Sharing Classes

Isolating class loading domains is nice. Our applications will run happily and safe. But very slowly, when it comes to interacting with each other. This is because each interaction involves passing arguments by value, which means serialization, which means overhead.

We're sometimes (actually quite often) faced with the situation where in we would like to allow applications to share classes. We know precisely, for example, that in our environment, application A and B, otherwise independent, will always use the same version of the utility library and doing so, they could pass references among themselves without any problem. The added benefit in this case is that the invocations will be faster, given the fact serialization is cut out.

One word of caution though. In this situation, if we hot redeploy the utility library, we also must re-deploy the application A and B: the current A and B classes used direct references to the utility classes, so they're tainted forever, they won't ever be able to use the new utility classes.

JBoss makes possible for applications to share classes. JBoss 3.x does that by default. JBoss 4.0 does this for the "standard" configuration, but maintains class namespace isolation between applications for its "default" configuration. JBoss 4.0.1 reverts to the 3.x convention.

The class loader parent for each UCL is a NoAnnotationClassLoader instance. The NoAnnotationClassLoader extends URLClassLoader. A singleton NoAnnotationClassLoader instance is created during the server's boot process and its job is to define classes available in the $JBOSS_HOME/lib libraries (ex: commons-logging.jar, concurrent.jar, dom4j.jar, jboss-common.jar, jboss-jmx.jar, jboss-system.jar, log4j-boot.jar, xercesImpl.jar, etc.). The NoAnnotationClassLoader's parent is the system class loader (sun.misc.Launcher$AppClassLoader).

When a new UCL is created and associated with the repository, it contributes to the repository a map of packages it can potentially serve classes from. It doesn't add any class to the repository's class cache yet, because nobody has requested any class at this stage. The repository just walks through the class loader's URL to see what packages that UCL is capable of handling. So, the UCL just declares that it can potentially serve classes from the packages that are present in its classpath.

When requested to load a class, a UCL overrides the standard Java2 class loading model by first trying to load a class from its associated repository's cache. If it doesn't find it there, it delegates the task of loading the class to the first UCL associated with the repository that declared it can load that class. The order in which the UCLs have been added to the repository becomes important, because this is what defines "first" in this context. If no "available" UCL is found, the initiating UCL falls back to the standard Java2 parent delegation. This explains why you are still able to use "java.lang.String", for example.

At the end of this process, if no class definition is found in the bootstrap libraries, in the $JBOSS_HOME/lib libraries nor among the libraries associated with the repository's UCL, the UCL throws a ClassNotFoundException. However, if one of the pair UCL is able to load the class, the class will be added to the repository's class cache and from this moment on, it will be returned to any UCL requesting it. Even if the Java bootstrap packages or$JAVA_HOME/lib packages are not added to the repository's package map, the classes belonging to those packages can be loaded through the process described above and they are added to the repository too. This explains why you'll find "java.lang.String" in the repository.

Class sharing can be turned off. J2EE-style class namespace isolation is available. You get an "isolated" application by scoping the application's deployment. At the JBoss class loading management system level, scoping translates into creating a child repository. A scoped application still can load the classes present in the classpaths of the UCLs or the root repository. Depending on whether repository's "Java2ParentDelegation" flag is turned on or off, a scoped application even has access to the class instances available in the root repository's cache. However, sibling child repositories can never share classes.

### Case 2. The Utility.class is present both in the application's archive AND server/default/lib. The deployment is non-scoped.

The short story: The version of the class available in server/default/lib/utility.jar will be used by the new deployment. The version of the class packed with the deployment will be ignored.

The key element here is that when getPackageClassLoaders() is invoked on the repository, the method calls returns two potential classloaders that can load org.useful.Utility: UCL0 and UCL1. The UCL0 is chosen, because it was added to the repository before UCL1 and it will be used to load org.useful.Utility.

### Case 3. The Utility.class is present both in the application's archive AND server/default/lib. The deployment is scoped and Java2ParentDelegation is turned off (default).

The short story: The utility class is loaded from the application's archive.

Because Java2ParentDelegation is turned off by default, the Step (1.1) is never executed, parentRepository.getCachedClass() never gets called, so the UCL doesn't have access to the repository's cached classes.

Always the HierarchialLoaderRepository's class loaders take precedence over the parent's (their "order" is lower). For the case depicted above, UCL1 is the preferred class loader.

### Case 4. The Utility.class is present both in the application's archive AND server/default/lib. The deployment is scoped, but java2ParentDelegation is turned on.

When Java2ParentDelegation is turned on, the Step (1.1) is executed, and if a cached class is found in the parent repository, it is returned and the process stops here.

Within the scope of the call to getPackageClassLoaders() at Step (3), the child repository also calls getPackageClassLoaders() on its parent, but does not include into the returned class loader set a UCL with a parent to the system class loader. If there are no class loaders in the repository capable of handling the request ask the class loader itself in the event that its parent(s) can load the class (repository.loadClassFromClassLoader())

The HierarchialLoaderRepository's class loaders take precedence over the parent's (their "order" is lower). For the case depicted above, UCL1 is the preferred class loader.

Question: What happens if the parent delegation is true and a classloader already loaded the class in the parent repository's class cache? Answer: My scoped application will use the already loaded class from the parent repository's class cache.

### TO_DO

• Create similar use cases for WARs. Deploying a WAR involves an extra class loader (WebappClassLoader) created by the servlet container, that can be independently configured using the WAR's jboss-web.xml.

• Explain how UCL3 are created, by whom and why. Explain the relationship between UCL created during an EAR deployment. Multiple UCLs are created (the one corresponding to the EAR and then for the embedded JARs, WARs, etc).

• More details on Java2ParentDelegation flag. Make it clear that it only affects the relationship between repositories not classloaders. If a class is not found in any repository, then the standard delegation model is invoked (i.e. the parent classloader is asked to find the class).

• 本文已收录于以下专栏：

• Isildur2010
• 2014年03月17日 13:50
• 938

## IK 分词器 2012 FF 版本取消了 org.wltea.analyzer.solr.IKTokenizerFactory 类【导致只能使用ik分词器来进行分词，无法使用solr自带的其它过滤方式

• buster2014
• 2015年08月12日 15:00
• 1601

## spring中定时器编写示例

spring中定时器编写示例
• yhj19920417
• 2017年05月16日 15:35
• 358

## 报错 Error:scalac: missing or invalid dependency detected while loading class file ***

• BornZhu
• 2017年12月06日 20:31
• 118

## Loading class com.mysql.jdbc.Driver'. This is deprecated. The new driver class is com.mysql.cj.jdb

• anaini1314
• 2017年05月04日 08:55
• 5185

• u012965373
• 2016年03月24日 08:33
• 915

## JBOSS 中常见错误

• xue_feitian
• 2009年12月08日 14:19
• 3399

• chenfs1992
• 2014年03月18日 09:57
• 3301

## 第一个spring小demo

• magi1201
• 2015年04月02日 22:13
• 2824