-
Bug
-
Resolution: Not an Issue
-
P4
-
None
-
7u76, 8u31
-
x86_64
-
linux
FULL PRODUCT VERSION :
Multiple versions, JDK 1.7.0_45 and 1.7.0_76 tested, also, 1.6.0_45 and
openjdk version "1.8.0_31"
OpenJDK Runtime Environment (build 1.8.0_31-b13)
OpenJDK 64-Bit Server VM (build 25.31-b07, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Multiple attempted, Mac OSX 10.10 (Mavericks), Linux x64 CentOS
EXTRA RELEVANT SYSTEM CONFIGURATION :
Reproduced on a 4-core Linux VM and an 8-core MacBook Pro (2013)
A DESCRIPTION OF THE PROBLEM :
The situation is extremely specific. The application has 300+ running threads. A pair of ClassLoaders is used to load the relevant application classes. Thread [2] wants to load and use the class while Thread [1] is currently loading it.
The key feature is that ClassLoader [A].findClass() delegates to [B] using Class.forName(), and then [B] delegates back to [A].findClass() for application-specific reasons. The second delegation to [A] either defines the class or throws a ClassNotFoundException. There isn't the risk of infinite recursion in this configuration.
When [B] returns the defined class to Class.forName(), the native code there attempts to initialize the newly defined class. ClassLoader [A] is still locked, as expected, by Thread [1]. This is where the bug occurs. Before Thread [1] can initialize the class, Thread [2] somehow sneaks in and begins initialization. However, the <clinit> requires [A] to load additional classes, and the lock on [A] is held by Thread [1].
The result is that [2] is blocking in <clinit> on [1]'s lock on [A], while [1] is stuck doing an Object.wait() in Class.forName(), waiting for [2] to finish <clinit>.
The issue does NOT occur if [B].loadClass is invoked directly instead of using Class.forName(), or if Class.forName() is invoked with "false" for initialization.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
(1) Create ClassLoaders as described above
(2) Create two classes to be loaded by the ClassLoaders, one of which creates a new instance of the other in its static initializer
(3) Create 300 threads that each attempt construct a new instance of the class with an initializer
I have demo code that locks up about 2 out of 3 times.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The expected result is that Class.forName() would retain the lock on ClassLoader [A] in Thread [1], perform the initialization in Thread [1], and then release the lock. Thread [2] would then proceed normally after the class has been initialized.
ACTUAL -
The two threads deadlocked and the classloader became unusable.
REPRODUCIBILITY :
This bug can be reproduced often.
Multiple versions, JDK 1.7.0_45 and 1.7.0_76 tested, also, 1.6.0_45 and
openjdk version "1.8.0_31"
OpenJDK Runtime Environment (build 1.8.0_31-b13)
OpenJDK 64-Bit Server VM (build 25.31-b07, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Multiple attempted, Mac OSX 10.10 (Mavericks), Linux x64 CentOS
EXTRA RELEVANT SYSTEM CONFIGURATION :
Reproduced on a 4-core Linux VM and an 8-core MacBook Pro (2013)
A DESCRIPTION OF THE PROBLEM :
The situation is extremely specific. The application has 300+ running threads. A pair of ClassLoaders is used to load the relevant application classes. Thread [2] wants to load and use the class while Thread [1] is currently loading it.
The key feature is that ClassLoader [A].findClass() delegates to [B] using Class.forName(), and then [B] delegates back to [A].findClass() for application-specific reasons. The second delegation to [A] either defines the class or throws a ClassNotFoundException. There isn't the risk of infinite recursion in this configuration.
When [B] returns the defined class to Class.forName(), the native code there attempts to initialize the newly defined class. ClassLoader [A] is still locked, as expected, by Thread [1]. This is where the bug occurs. Before Thread [1] can initialize the class, Thread [2] somehow sneaks in and begins initialization. However, the <clinit> requires [A] to load additional classes, and the lock on [A] is held by Thread [1].
The result is that [2] is blocking in <clinit> on [1]'s lock on [A], while [1] is stuck doing an Object.wait() in Class.forName(), waiting for [2] to finish <clinit>.
The issue does NOT occur if [B].loadClass is invoked directly instead of using Class.forName(), or if Class.forName() is invoked with "false" for initialization.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
(1) Create ClassLoaders as described above
(2) Create two classes to be loaded by the ClassLoaders, one of which creates a new instance of the other in its static initializer
(3) Create 300 threads that each attempt construct a new instance of the class with an initializer
I have demo code that locks up about 2 out of 3 times.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The expected result is that Class.forName() would retain the lock on ClassLoader [A] in Thread [1], perform the initialization in Thread [1], and then release the lock. Thread [2] would then proceed normally after the class has been initialized.
ACTUAL -
The two threads deadlocked and the classloader became unusable.
REPRODUCIBILITY :
This bug can be reproduced often.