Home | History | Annotate | Download | only in lang
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package java.lang;
     18 
     19 import android.system.Os;
     20 import android.system.OsConstants;
     21 import dalvik.system.VMRuntime;
     22 import java.lang.ref.FinalizerReference;
     23 import java.lang.ref.Reference;
     24 import java.lang.ref.ReferenceQueue;
     25 import java.util.concurrent.atomic.AtomicBoolean;
     26 import java.util.concurrent.atomic.AtomicInteger;
     27 import java.util.concurrent.TimeoutException;
     28 import libcore.util.EmptyArray;
     29 
     30 /**
     31  * Calls Object.finalize() on objects in the finalizer reference queue. The VM
     32  * will abort if any finalize() call takes more than the maximum finalize time
     33  * to complete.
     34  *
     35  * @hide
     36  */
     37 public final class Daemons {
     38     private static final int NANOS_PER_MILLI = 1000 * 1000;
     39     private static final int NANOS_PER_SECOND = NANOS_PER_MILLI * 1000;
     40     private static final long MAX_FINALIZE_NANOS = 10L * NANOS_PER_SECOND;
     41 
     42     public static void start() {
     43         ReferenceQueueDaemon.INSTANCE.start();
     44         FinalizerDaemon.INSTANCE.start();
     45         FinalizerWatchdogDaemon.INSTANCE.start();
     46         HeapTaskDaemon.INSTANCE.start();
     47     }
     48 
     49     public static void stop() {
     50         HeapTaskDaemon.INSTANCE.stop();
     51         ReferenceQueueDaemon.INSTANCE.stop();
     52         FinalizerDaemon.INSTANCE.stop();
     53         FinalizerWatchdogDaemon.INSTANCE.stop();
     54     }
     55 
     56     /**
     57      * A background task that provides runtime support to the application.
     58      * Daemons can be stopped and started, but only so that the zygote can be a
     59      * single-threaded process when it forks.
     60      */
     61     private static abstract class Daemon implements Runnable {
     62         private Thread thread;
     63         private String name;
     64 
     65         protected Daemon(String name) {
     66             this.name = name;
     67         }
     68 
     69         public synchronized void start() {
     70             if (thread != null) {
     71                 throw new IllegalStateException("already running");
     72             }
     73             thread = new Thread(ThreadGroup.systemThreadGroup, this, name);
     74             thread.setDaemon(true);
     75             thread.start();
     76         }
     77 
     78         public abstract void run();
     79 
     80         /**
     81          * Returns true while the current thread should continue to run; false
     82          * when it should return.
     83          */
     84         protected synchronized boolean isRunning() {
     85             return thread != null;
     86         }
     87 
     88         public synchronized void interrupt() {
     89             interrupt(thread);
     90         }
     91 
     92         public synchronized void interrupt(Thread thread) {
     93             if (thread == null) {
     94                 throw new IllegalStateException("not running");
     95             }
     96             thread.interrupt();
     97         }
     98 
     99         /**
    100          * Waits for the runtime thread to stop. This interrupts the thread
    101          * currently running the runnable and then waits for it to exit.
    102          */
    103         public void stop() {
    104             Thread threadToStop;
    105             synchronized (this) {
    106                 threadToStop = thread;
    107                 thread = null;
    108             }
    109             if (threadToStop == null) {
    110                 throw new IllegalStateException("not running");
    111             }
    112             interrupt(threadToStop);
    113             while (true) {
    114                 try {
    115                     threadToStop.join();
    116                     return;
    117                 } catch (InterruptedException ignored) {
    118                 } catch (OutOfMemoryError ignored) {
    119                     // An OOME may be thrown if allocating the InterruptedException failed.
    120                 }
    121             }
    122         }
    123 
    124         /**
    125          * Returns the current stack trace of the thread, or an empty stack trace
    126          * if the thread is not currently running.
    127          */
    128         public synchronized StackTraceElement[] getStackTrace() {
    129             return thread != null ? thread.getStackTrace() : EmptyArray.STACK_TRACE_ELEMENT;
    130         }
    131     }
    132 
    133     /**
    134      * This heap management thread moves elements from the garbage collector's
    135      * pending list to the managed reference queue.
    136      */
    137     private static class ReferenceQueueDaemon extends Daemon {
    138         private static final ReferenceQueueDaemon INSTANCE = new ReferenceQueueDaemon();
    139 
    140         ReferenceQueueDaemon() {
    141             super("ReferenceQueueDaemon");
    142         }
    143 
    144         @Override public void run() {
    145             while (isRunning()) {
    146                 Reference<?> list;
    147                 try {
    148                     synchronized (ReferenceQueue.class) {
    149                         while (ReferenceQueue.unenqueued == null) {
    150                             ReferenceQueue.class.wait();
    151                         }
    152                         list = ReferenceQueue.unenqueued;
    153                         ReferenceQueue.unenqueued = null;
    154                     }
    155                 } catch (InterruptedException e) {
    156                     continue;
    157                 } catch (OutOfMemoryError e) {
    158                     continue;
    159                 }
    160                 ReferenceQueue.enqueuePending(list);
    161             }
    162         }
    163     }
    164 
    165     private static class FinalizerDaemon extends Daemon {
    166         private static final FinalizerDaemon INSTANCE = new FinalizerDaemon();
    167         private final ReferenceQueue<Object> queue = FinalizerReference.queue;
    168         private final AtomicInteger progressCounter = new AtomicInteger(0);
    169         // Object (not reference!) being finalized. Accesses may race!
    170         private Object finalizingObject = null;
    171 
    172         FinalizerDaemon() {
    173             super("FinalizerDaemon");
    174         }
    175 
    176         @Override public void run() {
    177             // This loop may be performance critical, since we need to keep up with mutator
    178             // generation of finalizable objects.
    179             // We minimize the amount of work we do per finalizable object. For example, we avoid
    180             // reading the current time here, since that involves a kernel call per object.  We
    181             // limit fast path communication with FinalizerWatchDogDaemon to what's unavoidable: A
    182             // non-volatile store to communicate the current finalizable object, e.g. for
    183             // reporting, and a release store (lazySet) to a counter.
    184             // We do stop the  FinalizerWatchDogDaemon if we have nothing to do for a
    185             // potentially extended period.  This prevents the device from waking up regularly
    186             // during idle times.
    187 
    188             // Local copy of progressCounter; saves a fence per increment on ARM and MIPS.
    189             int localProgressCounter = progressCounter.get();
    190 
    191             while (isRunning()) {
    192                 try {
    193                     // Use non-blocking poll to avoid FinalizerWatchdogDaemon communication
    194                     // when busy.
    195                     FinalizerReference<?> finalizingReference = (FinalizerReference<?>)queue.poll();
    196                     if (finalizingReference != null) {
    197                         finalizingObject = finalizingReference.get();
    198                         progressCounter.lazySet(++localProgressCounter);
    199                     } else {
    200                         finalizingObject = null;
    201                         progressCounter.lazySet(++localProgressCounter);
    202                         // Slow path; block.
    203                         FinalizerWatchdogDaemon.INSTANCE.goToSleep();
    204                         finalizingReference = (FinalizerReference<?>)queue.remove();
    205                         finalizingObject = finalizingReference.get();
    206                         progressCounter.set(++localProgressCounter);
    207                         FinalizerWatchdogDaemon.INSTANCE.wakeUp();
    208                     }
    209                     doFinalize(finalizingReference);
    210                 } catch (InterruptedException ignored) {
    211                 } catch (OutOfMemoryError ignored) {
    212                 }
    213             }
    214         }
    215 
    216         @FindBugsSuppressWarnings("FI_EXPLICIT_INVOCATION")
    217         private void doFinalize(FinalizerReference<?> reference) {
    218             FinalizerReference.remove(reference);
    219             Object object = reference.get();
    220             reference.clear();
    221             try {
    222                 object.finalize();
    223             } catch (Throwable ex) {
    224                 // The RI silently swallows these, but Android has always logged.
    225                 System.logE("Uncaught exception thrown by finalizer", ex);
    226             } finally {
    227                 // Done finalizing, stop holding the object as live.
    228                 finalizingObject = null;
    229             }
    230         }
    231     }
    232 
    233     /**
    234      * The watchdog exits the VM if the finalizer ever gets stuck. We consider
    235      * the finalizer to be stuck if it spends more than MAX_FINALIZATION_MILLIS
    236      * on one instance.
    237      */
    238     private static class FinalizerWatchdogDaemon extends Daemon {
    239         private static final FinalizerWatchdogDaemon INSTANCE = new FinalizerWatchdogDaemon();
    240 
    241         private boolean needToWork = true;  // Only accessed in synchronized methods.
    242 
    243         FinalizerWatchdogDaemon() {
    244             super("FinalizerWatchdogDaemon");
    245         }
    246 
    247         @Override public void run() {
    248             while (isRunning()) {
    249                 if (!sleepUntilNeeded()) {
    250                     // We have been interrupted, need to see if this daemon has been stopped.
    251                     continue;
    252                 }
    253                 final Object finalizing = waitForFinalization();
    254                 if (finalizing != null && !VMRuntime.getRuntime().isDebuggerActive()) {
    255                     finalizerTimedOut(finalizing);
    256                     break;
    257                 }
    258             }
    259         }
    260 
    261         /**
    262          * Wait until something is ready to be finalized.
    263          * Return false if we have been interrupted
    264          * See also http://code.google.com/p/android/issues/detail?id=22778.
    265          */
    266         private synchronized boolean sleepUntilNeeded() {
    267             while (!needToWork) {
    268                 try {
    269                     wait();
    270                 } catch (InterruptedException e) {
    271                     // Daemon.stop may have interrupted us.
    272                     return false;
    273                 } catch (OutOfMemoryError e) {
    274                     return false;
    275                 }
    276             }
    277             return true;
    278         }
    279 
    280         /**
    281          * Notify daemon that it's OK to sleep until notified that something is ready to be
    282          * finalized.
    283          */
    284         private synchronized void goToSleep() {
    285             needToWork = false;
    286         }
    287 
    288         /**
    289          * Notify daemon that there is something ready to be finalized.
    290          */
    291         private synchronized void wakeUp() {
    292             needToWork = true;
    293             notify();
    294         }
    295 
    296         private synchronized boolean getNeedToWork() {
    297             return needToWork;
    298         }
    299 
    300         /**
    301          * Sleep for the given number of nanoseconds.
    302          * @return false if we were interrupted.
    303          */
    304         private boolean sleepFor(long durationNanos) {
    305             long startNanos = System.nanoTime();
    306             while (true) {
    307                 long elapsedNanos = System.nanoTime() - startNanos;
    308                 long sleepNanos = durationNanos - elapsedNanos;
    309                 long sleepMills = sleepNanos / NANOS_PER_MILLI;
    310                 if (sleepMills <= 0) {
    311                     return true;
    312                 }
    313                 try {
    314                     Thread.sleep(sleepMills);
    315                 } catch (InterruptedException e) {
    316                     if (!isRunning()) {
    317                         return false;
    318                     }
    319                 } catch (OutOfMemoryError ignored) {
    320                     if (!isRunning()) {
    321                         return false;
    322                     }
    323                 }
    324             }
    325         }
    326 
    327 
    328         /**
    329          * Return an object that took too long to finalize or return null.
    330          * Wait MAX_FINALIZE_NANOS.  If the FinalizerDaemon took essentially the whole time
    331          * processing a single reference, return that reference.  Otherwise return null.
    332          */
    333         private Object waitForFinalization() {
    334             long startCount = FinalizerDaemon.INSTANCE.progressCounter.get();
    335             // Avoid remembering object being finalized, so as not to keep it alive.
    336             if (!sleepFor(MAX_FINALIZE_NANOS)) {
    337                 // Don't report possibly spurious timeout if we are interrupted.
    338                 return null;
    339             }
    340             if (getNeedToWork() && FinalizerDaemon.INSTANCE.progressCounter.get() == startCount) {
    341                 // We assume that only remove() and doFinalize() may take time comparable to
    342                 // MAX_FINALIZE_NANOS.
    343                 // We observed neither the effect of the gotoSleep() nor the increment preceding a
    344                 // later wakeUp. Any remove() call by the FinalizerDaemon during our sleep
    345                 // interval must have been followed by a wakeUp call before we checked needToWork.
    346                 // But then we would have seen the counter increment.  Thus there cannot have
    347                 // been such a remove() call.
    348                 // The FinalizerDaemon must not have progressed (from either the beginning or the
    349                 // last progressCounter increment) to either the next increment or gotoSleep()
    350                 // call.  Thus we must have taken essentially the whole MAX_FINALIZE_NANOS in a
    351                 // single doFinalize() call.  Thus it's OK to time out.  finalizingObject was set
    352                 // just before the counter increment, which preceded the doFinalize call.  Thus we
    353                 // are guaranteed to get the correct finalizing value below, unless doFinalize()
    354                 // just finished as we were timing out, in which case we may get null or a later
    355                 // one.  In this last case, we are very likely to discard it below.
    356                 Object finalizing = FinalizerDaemon.INSTANCE.finalizingObject;
    357                 sleepFor(NANOS_PER_SECOND / 2);
    358                 // Recheck to make it even less likely we report the wrong finalizing object in
    359                 // the case which a very slow finalization just finished as we were timing out.
    360                 if (getNeedToWork()
    361                         && FinalizerDaemon.INSTANCE.progressCounter.get() == startCount) {
    362                     return finalizing;
    363                 }
    364             }
    365             return null;
    366         }
    367 
    368         private static void finalizerTimedOut(Object object) {
    369             // The current object has exceeded the finalization deadline; abort!
    370             String message = object.getClass().getName() + ".finalize() timed out after "
    371                     + (MAX_FINALIZE_NANOS / NANOS_PER_SECOND) + " seconds";
    372             Exception syntheticException = new TimeoutException(message);
    373             // We use the stack from where finalize() was running to show where it was stuck.
    374             syntheticException.setStackTrace(FinalizerDaemon.INSTANCE.getStackTrace());
    375             Thread.UncaughtExceptionHandler h = Thread.getDefaultUncaughtExceptionHandler();
    376             // Send SIGQUIT to get native stack traces.
    377             try {
    378                 Os.kill(Os.getpid(), OsConstants.SIGQUIT);
    379                 // Sleep a few seconds to let the stack traces print.
    380                 Thread.sleep(5000);
    381             } catch (Exception e) {
    382                 System.logE("failed to send SIGQUIT", e);
    383             } catch (OutOfMemoryError ignored) {
    384                 // May occur while trying to allocate the exception.
    385             }
    386             if (h == null) {
    387                 // If we have no handler, log and exit.
    388                 System.logE(message, syntheticException);
    389                 System.exit(2);
    390             }
    391             // Otherwise call the handler to do crash reporting.
    392             // We don't just throw because we're not the thread that
    393             // timed out; we're the thread that detected it.
    394             h.uncaughtException(Thread.currentThread(), syntheticException);
    395         }
    396     }
    397 
    398     // Adds a heap trim task to the heap event processor, not called from java. Left for
    399     // compatibility purposes due to reflection.
    400     public static void requestHeapTrim() {
    401         VMRuntime.getRuntime().requestHeapTrim();
    402     }
    403 
    404     // Adds a concurrent GC request task ot the heap event processor, not called from java. Left
    405     // for compatibility purposes due to reflection.
    406     public static void requestGC() {
    407         VMRuntime.getRuntime().requestConcurrentGC();
    408     }
    409 
    410     private static class HeapTaskDaemon extends Daemon {
    411         private static final HeapTaskDaemon INSTANCE = new HeapTaskDaemon();
    412 
    413         HeapTaskDaemon() {
    414             super("HeapTaskDaemon");
    415         }
    416 
    417         // Overrides the Daemon.interupt method which is called from Daemons.stop.
    418         public synchronized void interrupt(Thread thread) {
    419             VMRuntime.getRuntime().stopHeapTaskProcessor();
    420         }
    421 
    422         @Override public void run() {
    423             synchronized (this) {
    424                 if (isRunning()) {
    425                   // Needs to be synchronized or else we there is a race condition where we start
    426                   // the thread, call stopHeapTaskProcessor before we start the heap task
    427                   // processor, resulting in a deadlock since startHeapTaskProcessor restarts it
    428                   // while the other thread is waiting in Daemons.stop().
    429                   VMRuntime.getRuntime().startHeapTaskProcessor();
    430                 }
    431             }
    432             // This runs tasks until we are stopped and there is no more pending task.
    433             VMRuntime.getRuntime().runHeapTasks();
    434         }
    435     }
    436 }
    437