Name: mf23781 Date: 05/06/98
*Symptoms:
A deadlock can occur in the following scenario:
[1] A thread T is running and gets a monitor lock L, either through a
synchronized block or method.
[2] An object X with a finalize() method is garbage collected and added to the
FinalizeMeQ queue.
[3] Another thread S is running runFinalizaion0() in
src/share/java/runtime/finalize.c. S can be the system Finalizer thread, a
regular thread which calls Runtime.runFinalization() explicitly or a regular
thread which can not get enough memory and triggers the garbage
collection and finaliztion implicitly.
[4] S gets X from the queue and calls its finalize() method. In X.finalize()
it needs the same lock L being held by thread T, so S is blocked (monitor
wait).
[5] Now T also calls runFinalization, either explicitly or implicitly. T finds
out that there is another thread doing the finalization now, so T waits
on an internal lock FINALMEQ (waiting to be notified by S when S is
done).
[6] Now both T and S are blocked and deadlock occurs.
To fix the problem, either
(1) any finalize() method can not have a sychronized block or make a
synchronized method call.
or
(2) T should not wait in step [5]. Instead T should return with false.
(1) is too restrictive, e.g. the finalize() method may want to call
Hashtable.remove() to free a resource.
(2) sounds like a possible fix. In most cases, T will get enough memory
through soft references and heap expansion if necessary, and eventually release the lock L.
Enclosed below is a simple test program which demonstrate this problem.
public class Test
{
static synchronized void println()
{ //synchronized class method
System.out.println("Test.println() called by thread " + Thread.currentThread());
}
protected void finalize() throws Throwable
{ //finalizer method
System.out.println("finalize() of object " + this + " called by thread " + Thread.currentThread());
println(); //invoke synchronized class method within the finalize method
}
static synchronized void foo()
{ //synchronized class method
//when we reach here the class object of Test is locked by the main thread
//allocate an instance of Test then free it immediately
Test t1 = new Test();
try
{
t1.finalize(); //resolve and test the finalize() method, not blocked since the main thread owns the lock
}
catch (Throwable t) { }
t1 = null; //t1 will be added to the FinalizeMeQ
Runtime.getRuntime().gc(); //notify/wakeup the Finalizer thread
try
{ //yield to the Finalizer thread
Thread.sleep(2000);
}
catch (Exception e)
{
}
//The Finalizer thread will run the finalize() method of t1 and is blocked at Test.println()
//to wait for the lock of Test class object (owned by the main thread).
//The main thread will resume to execute the lines below.
t1 = new Test(); //allocate another instance of Test
t1 = null; //free it immediately such that the FinalizeMeQ is not empty
byte[] b = new byte[(int) Runtime.getRuntime().freeMemory()]; //allocate all free memory
//Due to out of memory, the main thread implicitly runs runFinalization() but is blocked at
//FinalizeMeQ.wait() (to be notified by the Finalizer thread).
//As a result, both threads are deadlocked.
b = null; //this line is necessary, otherwise the compiler will optimize out b = new byte[...]
}
public static void main(String[] args)
{
foo();
}
}
======================================================================
- duplicates
-
JDK-4091873 (refs) Calling a synchronized method from finalize() can lock VM
-
- Closed
-