Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4699981

ClassCircularityError thrown without reason during class loading

XMLWordPrintable

    • b51
    • x86, sparc
    • windows, windows_2000

        Name: nt126004 Date: 06/10/2002


        FULL PRODUCT VERSION :
        java version "1.3.1"
        Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1-b24)
        Java HotSpot(TM) Client VM (build 1.3.1-b24, mixed mode)

        and

        java version "1.4.0"
        Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-b92)
        Java HotSpot(TM) Client VM (build 1.4.0-b92, mixed mode)

        FULL OPERATING SYSTEM VERSION :
        Microsoft Windows 2000 [Version 5.00.2195]

        A DESCRIPTION OF THE PROBLEM :
        This problem happens both in JDK 1.3.1 and in 1.4.0,
        Windows 2000.

        When 2 threads are loading the same class with the same
        classloader, and somehow one of the 2 threads releases the
        synchronization lock on the classloader, the JVM code
        throws ClassCircularityError, mistakenly.

        Refer to the JVM class (in systemDictionary.cpp) and method
        SystemDictionary::resolve_instance_class_or_null(...) for
        where the bug is originating.

        Refer also to RFE #4670071. As explained there, in JBoss
        3.0 (http://www.jboss.org) a new classloading model is
        used, not based on the tree model, that gives important
        features such as hot-deploy and modularity. Because of the
        synchronization on the classloader, JBoss' classloading
        model (or any other classloading mechanism not based on the
        tree model) may suffers of deadlocks. In JBoss 3.0 the
        deadlock issue has been resolved, releasing the classloader
        lock, but this solution showed up the ClassCircularityError
        bug, this time in JVM code, and hence with no solution.

        Probably the JVM code should throw ClassCircularityError if
        a placeholder is found by the same thread that put it, not
        if another thread is coming in asking for the same class
        with the same classloader.

        Note that the problem originates by calls that trigger a
        call to the native code in
        SystemDictionary::resolve_instance_class_or_null(...); from
        investigation I was able to find these methods:

        1. Class.defineClass
        2. Class.loadClassInternal
        3. Class.newInstance (and Constructor.newInstance)
        4. Class.forName

        There may be other, the code submitted only shows the
        problem with 3., but in JBoss the problem is seen it with
        all of the 4 cases listed above.

        In the code submitted, URLClassLoader has been subclassed
        only to make a reproducible test case, but in real code
        there is no need to subclass it to get the problem (only
        will happen only in certain situation/thread timing).

        It would be nice that this bug and RFE #4670071 will go on
        in parallell, to give more freedom to the classloading
        mechanism.

        STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
        1. Make 4 files of the submitted code, namely Base.java,
        Derived.java, Support.java and Main.java.
        2. Compile the files, for example in directory classes/
        3. java -cp classes Main

        EXPECTED VERSUS ACTUAL BEHAVIOR :
        Expected result: no errors, the 2 threads are able to load
        the classes
        Actual Result: ClassCircularityError is thrown.

        ERROR MESSAGES/STACK TRACES THAT OCCUR :
        java.lang.ClassCircularityError: Base
                at Support.<init>(Support.java:7)
                at java.lang.Class.newInstance0(Native Method)
                at java.lang.Class.newInstance(Class.java:237)
                at Main$Run1.run(Main.java:97)
                at java.lang.Thread.run(Thread.java:484)

        This bug can be reproduced always.

        ---------- BEGIN SOURCE ----------
        public class Base {}

        public class Derived extends Base {}

        public class Support
        {
           private Base base = new Base();
        }


        import java.net.URL;
        import java.net.URLClassLoader;

        /**
         *
         * @version $Revision$
         */
        public class Main
        {
           public static void main(String[] args) throws Exception
           {
              new Main();
           }

           private Object lock = new Object();

           public Main() throws Exception
           {
              URL location = getClass().getProtectionDomain().getCodeSource
        ().getLocation();
              URLLoader loader = new URLLoader(new URL[] {location}, getClass
        ().getClassLoader().getParent());

              Class cls = loader.loadClass("Support");

              Thread t1 = new Thread(new Run1(cls));
              t1.start();

              Thread.sleep(1000);

              // Load Derived, will trigger a loadClassInternal for Base
              loader.loadClass("Derived");
           }

           public class URLLoader extends URLClassLoader
           {
              private boolean m_firstTime = true;

              public URLLoader(URL[] urls, ClassLoader parent)
              {
                 super(urls, parent);
              }

              public Class loadClass(String name) throws ClassNotFoundException
              {
                 if (name.equals("Base"))
                 {
                    if (m_firstTime)
                    {
                       m_firstTime = false;

                       // Notify the other thread
                       synchronized (lock)
                       {
                          lock.notifyAll();
                       }

                       // Wait on the classloader to have the JVM throw
        ClassCircularityError
                       try
                       {
                          synchronized (this)
                          {
                             wait(5000);
                          }
                       }
                       catch (InterruptedException ignored)
                       {
                       }
                    }
                 }
                 return super.loadClass(name);
              }
           }

           public class Run1 implements Runnable
           {
              private Class cls;

              public Run1(Class cls)
              {
                 this.cls = cls;
              }

              public void run()
              {
                 synchronized (lock)
                 {
                    try
                    {
                       lock.wait();
                    }
                    catch (InterruptedException ignored) {}
                 }

                 // Trigger loadClassInternal for Base
                 try
                 {
                    cls.newInstance();
                 }
                 catch (Throwable x)
                 {
                    x.printStackTrace();
                 }
              }
           }
        }
        ---------- END SOURCE ----------

        CUSTOMER WORKAROUND :
        None, unfortunately.
        (Review ID: 148304)
        ======================================================================

              acorn Karen Kinnear (Inactive)
              nthompsosunw Nathanael Thompson (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

                Created:
                Updated:
                Resolved:
                Imported:
                Indexed: