Spring Framework
  1. Spring Framework
  2. SPR-2611

Class.forName() produces different results to ClassLoader.loadClass() in certain circumstances

    Details

    • Type: Improvement Improvement
    • Status: Closed
    • Priority: Minor Minor
    • Resolution: Fixed
    • Affects Version/s: 2.0 RC3
    • Fix Version/s: 2.0 final
    • Component/s: Core
    • Labels:
      None
    • Last commented by a User:
      false

      Description

      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!

        Activity

        Hide
        Steve Barham added a comment -

        Test case for the problem

        Show
        Steve Barham added a comment - Test case for the problem
        Hide
        Steve Barham added a comment -

        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

        Show
        Steve Barham added a comment - 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
        Hide
        Juergen Hoeller added a comment -

        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

        Show
        Juergen Hoeller added a comment - 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
        Hide
        Steve Barham added a comment -

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

        Show
        Steve Barham added a comment - No worries - thanks for the quick resolution on this one. Looking forward to Spring 2.0 final
        Hide
        Juergen Hoeller added a comment -

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

        Juergen

        Show
        Juergen Hoeller added a comment - You're welcome - feel free to give the next nightly snapshot a try in the meantime Juergen

          People

          • Assignee:
            Juergen Hoeller
            Reporter:
            Steve Barham
            Last updater:
            Trevor Marshall
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:
              Days since last comment:
              7 years, 31 weeks, 4 days ago