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

(refs) Deadlock of finalize and synchronised methods

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Duplicate
    • Icon: P3 P3
    • None
    • 1.1.6
    • hotspot
    • x86
    • windows_95



      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();
          }
      }





      ======================================================================

            mr Mark Reinhold
            miflemi Mick Fleming
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: