Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Class.forName() produces different results to ClassLoader.loadClass() in certain circumstances [SPR-2611] #7300

Closed
spring-projects-issues opened this issue Sep 19, 2006 · 5 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Sep 19, 2006

Steve Barham opened SPR-2611 and commented

I've been converting an application to use Spring, and have had a smooth
time of it so far. I have hit somewhat of a stumbling block, however, when
it comes to converting a plugin system which was implemented for the
application.

Background: previously, a new classloader would be created when a plugin was
initialised or updated. This classloader would be used to load a declared
class of a certain interface, instantiate it and call a plug() method.
Conversely, when the plugin was unplugged, an unplug() method would be
called, and the classloader released so that it could be GCed. So far, so
straightforward.

My aim in converting the plugin system was to use nested
ApplicationContexts, so that plugins could have access to beans declared in
the main system without having to specify any number of cumbersome
activation interfaces, and generally to benefit from using Spring. This
seemed to be a straightforward change, until I tested the plugin reloading
functionality. This monitors the JAR from which the plugin is deployed, and
when the filesize or timestamp changes triggers a reload by:

  • unplugging the plugin
  • closing its application context
  • making its classloader available for GC by releasing all application
    references
  • creating a new classloader
  • creating a new thread, with the context classloader being the new plugin
    classloader
  • creating a new application context

The problem is that the old class definition for the plugin was being used,
rather than the new class definition. I have confirmed through profiling
that no standard references to the plugin classloader exist after the plugin
has been unplugged; a weak reference is held to it by the
CachedIntrospectionResults class.

I have traced the 'problem' down to a single line of code in Spring, in
ClassUtils, line 160, where the following code is executed:

return Class.forName(name, false, classLoader);

Where name is the class of my plugin, and classLoader is confirmed to be the
new plugin classloader which points to the updated plugin classes.

When this is executed for the reloaded plugin application context, the
result is a class whose classloader is not the new plugin classloader passed
in the third parameter to Class.forName() - it is the old, supposedly
collectable instance of the plugin classloader. By constrast, if this line
is replaced with:

return classLoader.loadClass(name);

the correct behaviour is exhibited, i.e. the new plugin classloader which is
passed to ClassUtils.forName() is used to load the class.

I have tried to find a full description of the varying semantics of
Class.forName vs. classLoader.loadClass(), but have yet to find anything of
sufficient detail for me to follow this problem further. I can only confirm
that the old classloader is used by Class.forName despite it being available
for collection, as it is only referenced weakly by
CachedIntrospectionResults.

I can simulate the correct behaviour by invoking System.gc() some large
number of times when a plugin is unplugged, which seems to finalise the old
classloader and make Class.forName use the new one. This is obviously not a
desirable solution! :)


Affects: 2.0 RC3

Attachments:

Issue Links:

@spring-projects-issues
Copy link
Collaborator Author

Steve Barham commented

Test case for the problem

@spring-projects-issues
Copy link
Collaborator Author

Steve Barham commented

The test case emits:

<spring logging>
Plugin version 1
<spring logging>
Plugin version 1

where it should emit:

<spring logging>
Plugin version 1
<spring logging>
Plugin version 2

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

Thanks for pointing this out! I've done some research and found consistent reports about "ClassLoader.loadClass" being safer, since "Class.forName" has VM-specific (rather weird and unexpected) extra behavior.

Here's one interesting article that goes into quite some detail on this...
http://www.sdn.sap.com/irj/sdn/weblogs?blog=/pub/wlg/3655

As a consequence, all of Spring's class loading is done via "ClassLoader.loadClass(name)" rather than "Class.forName(name, true, ClassLoader)" now. Essentially, it's simply Spring's "ClassUtils.forName" implementation using the "loadClass" variant internally now.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Steve Barham commented

No worries - thanks for the quick resolution on this one. Looking forward to Spring 2.0 final :)

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

You're welcome - feel free to give the next nightly snapshot a try in the meantime :-)

Juergen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

2 participants