JDK-6501158 : Thread state is incorrect during class initialization procedure
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 6
  • Priority: P3
  • Status: Closed
  • Resolution: Not an Issue
  • OS: generic
  • CPU: generic
  • Submitted: 2006-12-06
  • Updated: 2021-11-09
  • Resolved: 2014-01-31
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 9
9Resolved
Related Reports
Relates :  
Description
the Java Language Spec 12.4.2 describes the detail Initalization procedure.  The step 2 says 

"2. If initialization is in progress for the class or interface by some other thread,
then wait on this Class object (which temporarily releases the lock). When
the current thread awakens from the wait, repeat this step."

I wrote a small program , it shows the thread state is not Thread.WAIT,  it's Thread.RUNNABLE on both solaris and windows.  see below.  When Thread t tries to read Init.done,  the state of t should be WAIT according to language spec.  however, it's RUNNABLE in fact. 

class Init{

        static volatile boolean done = false;

        static {
                Thread t = new Thread() {
                        public void run() {
                                System.out.println("enter thread");
                                done = true;
                        }
                };

                t.start();

                while(!done) {
                        try {
                                Thread.sleep(3000);
                        } catch (Exception e) {}

                        System.out.println("state " + t.getState());
                }
        }

        public static void main(String[] args) {}
}

Comments
Based on David Holmes' comment we're closing this as Not an issue
31-01-2014

Fast forward another 3.5 years and with PermGen removal the object used for locking during class initialization has changed again - it is now an int[0] constructed for this purpose. I'm inclined to simply close this as "not an issue".
12-06-2013

After changing the thread state to be correctly reported it was discovered that using the ThreadMXBean to get the ThreadInfo for that thread caused a SIGSEGV when doing Class.getName. It seems that the Class which is in the process of initialization may not have a correctly formed Classed object in some cases. Turning on TraceClassInitiaization avoids the crash. The problem is still being investigated. jstack output for problem thread and target thread is below: ----------------- t@2 ----------------- 0xfef4df57 _lwp_kill + 0x7 0xfeefadab raise + 0x1f 0xfeede8f9 abort + 0xcd 0xfead2c38 void os::abort(bool) + 0x30 0xfeb7595c void VMError::report_and_die() + 0x5c8 0xfe71b89e JVM_handle_solaris_signal + 0x73e 0xfe71b15a signalHandler + 0x26 0xfef4d31f __sighndlr + 0xf 0xfef43980 call_user_handler + 0x22b 0xfef43b00 sigacthandler + 0xbb 0xfe6b7877 char*symbolOopDesc::as_C_string()const + 0xb 0xfe7819a7 const char*symbolOopDesc::as_klass_external_name()const + 0xf 0xfe781af6 const char*Klass::external_name()const + 0x12 0xfe781fd3 JVM_GetClassName + 0x143 0xfb209e2c * java.lang.Class.getName0() bci:0 (Interpreted frame) 0xfb202da7 * java.lang.Class.getName() bci:9 line:570 (Interpreted frame) 0xfb202da7 * java.lang.management.LockInfo.<init>(java.lang.Object) bci:9 line:83 (Interpreted frame) 0xfb202f0d * java.lang.management.ThreadInfo.initialize(java.lang.Thread, int, java.lang.Object, java.lang.Thread, long, long, long, long, java.lang.StackTraceElement[], java.lang.management.MonitorInfo[], java.lang.management.LockInfo[]) bci:87 line:239 (Interpreted frame) 0xfb202f0d * java.lang.management.ThreadInfo.<init>(java.lang.Thread, int, java.lang.Object, java.lang.Thread, long, long, long, long, java.lang.StackTraceElement[]) bci:26 line:130 (Interpreted frame) 0xfb20023e <StubRoutines> 0xfe6f41ef void JavaCalls::call_helper(JavaValue*,methodHandle*,JavaCallArguments*,Thread*) + 0x1a3 0xfe6f4497 void os::os_exception_wrapper(void(*)(JavaValue*,methodHandle*,JavaCallArguments*,Thread*),JavaValue*,methodHandle*,JavaCallArguments*,Thread*) + 0x27 0xfe6f44cf void JavaCalls::call(JavaValue*,methodHandle,JavaCallArguments*,Thread*) + 0x2f 0xfe72d8aa void JavaCalls::call_special(JavaValue*,KlassHandle,symbolHandle,symbolHandle,JavaCallArguments*,Thread*) + 0x66 0xfeaab33e instanceOopDesc*Management::create_thread_info_instance(ThreadSnapshot*,Thread*) + 0x1c2 0xfeab1042 jmm_GetThreadInfo + 0x55e 0xfb013783 Java_sun_management_ThreadImpl_getThreadInfo0 + 0x2f 0xfb209e2c * sun.management.ThreadImpl.getThreadInfo0(long[], int, java.lang.management.ThreadInfo[]) bci:0 (Interpreted frame) 0xfb202f0d * sun.management.ThreadImpl.getThreadInfo(long[], int) bci:72 line:165 (Interpreted frame) 0xfb202da7 * sun.management.ThreadImpl.getThreadInfo(long) bci:44 line:125 (Interpreted frame) 0xfb203283 * ClinitDeadlockWithMonitoring$Tester.<clinit>() bci:52 line:31 (Interpreted frame) 0xfb20023e <StubRoutines> 0xfe6f41ef void JavaCalls::call_helper(JavaValue*,methodHandle*,JavaCallArguments*,Thread*) + 0x1a3 0xfe6f4497 void os::os_exception_wrapper(void(*)(JavaValue*,methodHandle*,JavaCallArguments*,Thread*),JavaValue*,methodHandle*,JavaCallArguments*,Thread*) + 0x27 0xfe6f44cf void JavaCalls::call(JavaValue*,methodHandle,JavaCallArguments*,Thread*) + 0x2f 0xfe71bb8e void instanceKlass::call_class_initializer_impl(instanceKlassHandle,Thread*) + 0xce 0xfe71bcbb void instanceKlass::call_class_initializer(Thread*) + 0x4f 0xfe6e2a27 void instanceKlass::initialize_impl(instanceKlassHandle,Thread*) + 0x50b 0xfe6db1d4 void instanceKlass::initialize(Thread*) + 0x6c 0xfea95cbb void LinkResolver::resolve_static_call(CallInfo&,KlassHandle&,symbolHandle,symbolHandle,KlassHandle,bool,bool,Thread*) + 0xd3 0xfe703d57 void LinkResolver::resolve_invokestatic(CallInfo&,constantPoolHandle,int,Thread*) + 0x67 0xfe6dd429 void LinkResolver::resolve_invoke(CallInfo&,Handle,constantPoolHandle,int,Bytecodes::Code,Thread*) + 0x71 0xfe6deb01 void InterpreterRuntime::resolve_invoke(JavaThread*,Bytecodes::Code) + 0x2b1 0xfb211cba * ClinitDeadlockWithMonitoring.main(java.lang.String[]) bci:15 line:54 (Interpreted frame) 0xfb20023e <StubRoutines> 0xfe6f41ef void JavaCalls::call_helper(JavaValue*,methodHandle*,JavaCallArguments*,Thread*) + 0x1a3 0xfe6f4497 void os::os_exception_wrapper(void(*)(JavaValue*,methodHandle*,JavaCallArguments*,Thread*),JavaValue*,methodHandle*,JavaCallArguments*,Thread*) + 0x27 0xfe6f44cf void JavaCalls::call(JavaValue*,methodHandle,JavaCallArguments*,Thread*) + 0x2f 0xfe77851b void jni_invoke_static(JNIEnv_*,JavaValue*,_jobject*,JNICallType,_jmethodID*,JNI_ArgumentPusher*,Thread*) + 0x1df 0xfe78161c jni_CallStaticVoidMethod + 0x154 0x080531ae JavaMain + 0xf76 0xfef4cf2f _thr_setup + 0x4e 0xfef4d220 _lwp_start ----------------- t@19 ----------------- 0xfef4e027 ___lwp_cond_wait + 0x7 0xfead6b2d void os::PlatformEvent::park() + 0xa9 0xfeb3161e void ObjectMonitor::wait(long long,bool,Thread*) + 0x266 0xfeb2fb25 void ObjectSynchronizer::waitUninterruptibly(Handle,long long,Thread*) + 0x69 0xfe6e266c void instanceKlass::initialize_impl(instanceKlassHandle,Thread*) + 0x150 0xfe6db1d4 void instanceKlass::initialize(Thread*) + 0x6c 0xfea95cbb void LinkResolver::resolve_static_call(CallInfo&,KlassHandle&,symbolHandle,symbolHandle,KlassHandle,bool,bool,Thread*) + 0xd3 0xfe703d57 void LinkResolver::resolve_invokestatic(CallInfo&,constantPoolHandle,int,Thread*) + 0x67 0xfe6dd429 void LinkResolver::resolve_invoke(CallInfo&,Handle,constantPoolHandle,int,Bytecodes::Code,Thread*) + 0x71 0xfe6deb01 void InterpreterRuntime::resolve_invoke(JavaThread*,Bytecodes::Code) + 0x2b1 0xfb211cba * ClinitDeadlockWithMonitoring$Tester$1.run() bci:18 line:17 (Interpreted frame) 0xfb2033e9 * java.lang.Thread.run() bci:11 line:637 (Interpreted frame) 0xfb20023e <StubRoutines> 0xfe6f41ef void JavaCalls::call_helper(JavaValue*,methodHandle*,JavaCallArguments*,Thread*) + 0x1a3 0xfe6f4497 void os::os_exception_wrapper(void(*)(JavaValue*,methodHandle*,JavaCallArguments*,Thread*),JavaValue*,methodHandle*,JavaCallArguments*,Thread*) + 0x27 0xfe6f44cf void JavaCalls::call(JavaValue*,methodHandle,JavaCallArguments*,Thread*) + 0x2f 0xfe76a759 void JavaCalls::call_virtual(JavaValue*,KlassHandle,symbolHandle,symbolHandle,JavaCallArguments*,Thread*) + 0xc1 0xfe7728ea void JavaCalls::call_virtual(JavaValue*,Handle,KlassHandle,symbolHandle,symbolHandle,Thread*) + 0x7e 0xfe78405a void thread_entry(JavaThread*,Thread*) + 0xd2 0xfe78058c void JavaThread::thread_main_inner() + 0x4c 0xfe780536 void JavaThread::run() + 0x182 0xfead255a java_start + 0xee 0xfef4cf2f _thr_setup + 0x4e 0xfef4d220 _lwp_start
12-06-2013

PUBLIC COMMENTS The submitter quotes the JLS with regard to the class initialization procedure and how synchronization is employed. In fact hotspot does not synchronize using the Class object monitor during class initialization - this is to avoid denial-of-service style attacks just by explicitly locking a Class object. The JLS is in the process of being updated to say that a "unique initialization lock " is used for class initialization, not necessarily the Class object's lock. This brings the spec into line with the hotspot implementation. The reason I mention this is that the monitor that hotspot uses is associated with the klassOop for the class. The monitor code sets current_waiting_monitor() or current_pending_monitor() as appropriate during wait() or monitor entry. The management code, via the ThreadService::ThreadSnapShot gets a hold of the object associated with the monitor for a blocked thread and assumes that the object is in fact the oop for a java.lang.Object. When the klassOop is treated as in instance oop and queried for its own class etc then we end up crashing the VM. The suggested fix correctly sets the thread state to "WAITING": Full thread dump Java HotSpot(TM) Tiered VM (1.7.0-internal-dh198349-fastdebug mixed mode): "Runner" prio=3 tid=0x08171800 nid=0xb in Object.wait() [0xcb99d000..0xcb99dbb0] java.lang.Thread.State: WAITING (on object monitor) but additional changes are need in ThreadSnapShot to discard the non-instance oop. (It seems JvmtiEnvBase::get_current_contended_monitor would need a similar modification). This seems to work and getThreadInfo simply reports eg: Current thread info: "Runner" Id=8 WAITING which seems okay. And getLockInfo() returns null. It is unclear however whether reporting this information actually violates the specification for these management API's. A thread is only WAITING when performing Object.wait(), in which case there must be an Object being waited upon and so LockInfo must return non-null information. Yet that is not the case here. It seems to me that while we can report the information above, it might be better to see whether the management specification can be enhanced to cover "waiting" on VM internal actions and to then report this circumstance as one of those. Note also that the existing hotspot code could already be susceptible to a crash due to the use of the klassOop monitor for class initialization. If the timing were just right, a call to getThreadInfo could see a thread blocked trying to acquire this monitor (not wait upon it) and that would be captured by the ThreadSnapshot and eventually cause a crash. The fact that the snapshot requires a safepoint makes it less likely that you would encounter the target thread while blocked on the monitor, as the monitor is only held for a short period during class initialization. I will await discussion with the management/monitoring folk before deciding how best to proceed with this CR.
18-11-2009

SUGGESTED FIX *** /tmp/geta13359 Wed Dec 6 16:00:35 2006 --- instanceKlass.cpp Wed Dec 6 15:43:53 2006 *************** *** 282,289 **** // If we were to use wait() instead of waitInterruptibly() then // we might end up throwing IE from link/symbol resolution sites // that aren't expected to throw. This would wreak havoc. See 6320309. ! while(this_oop->is_being_initialized() && !this_oop->is_reentrant_initialization(self)) { ! ol.waitUninterruptibly(CHECK); } // Step 3 --- 282,292 ---- // If we were to use wait() instead of waitInterruptibly() then // we might end up throwing IE from link/symbol resolution sites // that aren't expected to throw. This would wreak havoc. See 6320309. ! { ! JavaThreadInObjectWaitState jtiows((JavaThread*) self, false); ! while(this_oop->is_being_initialized() && !this_oop->is_reentrant_initialization(self)) { ! ol.waitUninterruptibly(CHECK); ! } } // Step 3
06-12-2006

EVALUATION Generally the entry points to the VM for things like Object.wait(), Thread.sleep() etc perform the updates to the monitoring state, as returned by Thread.getState(). The VM does not attempt to maintain this state directly when it performs blocking operations etc. In this case the use of Object.wait() is implicitly performed by the VM during class initialization and does not go through the normal Object.wait() VM entry point, consequently the thread state is not changed from RUNNABLE to WAITING.
06-12-2006