• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.compat.annotation.UnsupportedAppUsage;
20 import android.system.Os;
21 import android.system.OsConstants;
22 
23 import java.lang.invoke.MethodHandles;
24 import java.lang.invoke.VarHandle;
25 import java.lang.ref.Cleaner;
26 import java.lang.ref.FinalizerReference;
27 import java.lang.ref.Reference;
28 import java.lang.ref.ReferenceQueue;
29 import java.util.concurrent.CountDownLatch;
30 import java.util.concurrent.TimeoutException;
31 import java.util.concurrent.atomic.AtomicInteger;
32 import libcore.util.EmptyArray;
33 
34 import dalvik.system.VMRuntime;
35 import dalvik.system.VMDebug;
36 
37 import jdk.internal.ref.CleanerImpl;
38 
39 /**
40  * Calls Object.finalize() on objects in the finalizer reference queue. The VM
41  * will abort if any finalize() call takes more than the maximum finalize time
42  * to complete.
43  *
44  * @hide
45  */
46 public final class Daemons {
47     private static final int NANOS_PER_MILLI = 1000 * 1000;
48 
49     // This used to be final. IT IS NOW ONLY WRITTEN. We now update it when we look at the command
50     // line argument, for the benefit of mis-behaved apps that might read it.  SLATED FOR REMOVAL.
51     // There is no reason to use this: Finalizers should not rely on the value. If a finalizer takes
52     // appreciable time, the work should be done elsewhere.  Based on disassembly of Daemons.class,
53     // the value is effectively inlined, so changing the field never did have an effect.
54     // DO NOT USE. FOR ANYTHING. THIS WILL BE REMOVED SHORTLY.
55     @UnsupportedAppUsage
56     private static long MAX_FINALIZE_NANOS = 10L * 1000 * NANOS_PER_MILLI;
57 
58     private static final Daemon[] DAEMONS = new Daemon[] {
59             HeapTaskDaemon.INSTANCE,
60             ReferenceQueueDaemon.INSTANCE,
61             FinalizerDaemon.INSTANCE,
62             FinalizerWatchdogDaemon.INSTANCE,
63     };
64     private static final CountDownLatch POST_ZYGOTE_START_LATCH = new CountDownLatch(DAEMONS.length);
65     private static final CountDownLatch PRE_ZYGOTE_START_LATCH = new CountDownLatch(DAEMONS.length);
66 
67     private static boolean postZygoteFork = false;
68 
69     @UnsupportedAppUsage
start()70     public static void start() {
71         for (Daemon daemon : DAEMONS) {
72             daemon.start();
73         }
74     }
75 
startPostZygoteFork()76     public static void startPostZygoteFork() {
77         postZygoteFork = true;
78         for (Daemon daemon : DAEMONS) {
79             daemon.startPostZygoteFork();
80         }
81     }
82 
83     @UnsupportedAppUsage
stop()84     public static void stop() {
85         for (Daemon daemon : DAEMONS) {
86             daemon.stop();
87         }
88     }
89 
waitForDaemonStart()90     private static void waitForDaemonStart() throws Exception {
91         if (postZygoteFork) {
92             POST_ZYGOTE_START_LATCH.await();
93         } else {
94             PRE_ZYGOTE_START_LATCH.await();
95         }
96     }
97 
98     /**
99      * A background task that provides runtime support to the application.
100      * Daemons can be stopped and started, but only so that the zygote can be a
101      * single-threaded process when it forks.
102      */
103     private static abstract class Daemon implements Runnable {
104         @UnsupportedAppUsage
105         private Thread thread;
106         private String name;
107         private boolean postZygoteFork;
108 
Daemon(String name)109         protected Daemon(String name) {
110             this.name = name;
111         }
112 
113         @UnsupportedAppUsage
start()114         public synchronized void start() {
115             startInternal();
116         }
117 
startPostZygoteFork()118         public synchronized void startPostZygoteFork() {
119             postZygoteFork = true;
120             startInternal();
121         }
122 
startInternal()123         public void startInternal() {
124             if (thread != null) {
125                 throw new IllegalStateException("already running");
126             }
127             thread = new Thread(ThreadGroup.systemThreadGroup, this, name);
128             thread.setDaemon(true);
129             thread.setSystemDaemon(true);
130             thread.start();
131         }
132 
run()133         public final void run() {
134             if (postZygoteFork) {
135                 // We don't set the priority before the Thread.start() call above because
136                 // Thread.start() will call SetNativePriority and overwrite the desired native
137                 // priority. We (may) use a native priority that doesn't have a corresponding
138                 // java.lang.Thread-level priority (native priorities are more coarse-grained.)
139                 VMRuntime.getRuntime().setSystemDaemonThreadPriority();
140                 POST_ZYGOTE_START_LATCH.countDown();
141             } else {
142                 PRE_ZYGOTE_START_LATCH.countDown();
143             }
144             try {
145                 runInternal();
146             } catch (Throwable ex) {
147                 // Should never happen, but may not o.w. get reported, e.g. in zygote.
148                 // Risk logging redundantly, rather than losing it.
149                 System.logE("Uncaught exception in system thread " + name, ex);
150                 throw ex;
151             }
152         }
153 
runInternal()154         public abstract void runInternal();
155 
156         /**
157          * Returns true while the current thread should continue to run; false
158          * when it should return.
159          */
160         @UnsupportedAppUsage
isRunning()161         protected synchronized boolean isRunning() {
162             return thread != null;
163         }
164 
interrupt()165         public synchronized void interrupt() {
166             interrupt(thread);
167         }
168 
interrupt(Thread thread)169         public synchronized void interrupt(Thread thread) {
170             if (thread == null) {
171                 throw new IllegalStateException("not running");
172             }
173             thread.interrupt();
174         }
175 
176         /**
177          * Waits for the runtime thread to stop. This interrupts the thread
178          * currently running the runnable and then waits for it to exit.
179          */
180         @UnsupportedAppUsage
stop()181         public void stop() {
182             Thread threadToStop;
183             synchronized (this) {
184                 threadToStop = thread;
185                 thread = null;
186             }
187             if (threadToStop == null) {
188                 throw new IllegalStateException("not running");
189             }
190             interrupt(threadToStop);
191             while (true) {
192                 try {
193                     threadToStop.join();
194                     return;
195                 } catch (InterruptedException ignored) {
196                 } catch (OutOfMemoryError ignored) {
197                     // An OOME may be thrown if allocating the InterruptedException failed.
198                 }
199             }
200         }
201 
202         /**
203          * Returns the current stack trace of the thread, or an empty stack trace
204          * if the thread is not currently running.
205          */
getStackTrace()206         public synchronized StackTraceElement[] getStackTrace() {
207             return thread != null ? thread.getStackTrace() : EmptyArray.STACK_TRACE_ELEMENT;
208         }
209     }
210 
211     /**
212      * This heap management thread moves elements from the garbage collector's
213      * pending list to the managed reference queue.
214      */
215     private static class ReferenceQueueDaemon extends Daemon {
216         @UnsupportedAppUsage
217         private static final ReferenceQueueDaemon INSTANCE = new ReferenceQueueDaemon();
218 
219         // Monitored by FinalizerWatchdogDaemon to make sure we're still working.
220         private final AtomicInteger progressCounter = new AtomicInteger(0);
221 
ReferenceQueueDaemon()222         ReferenceQueueDaemon() {
223             super("ReferenceQueueDaemon");
224         }
225 
runInternal()226         @Override public void runInternal() {
227             FinalizerWatchdogDaemon.INSTANCE.monitoringNeeded(FinalizerWatchdogDaemon.RQ_DAEMON);
228             while (isRunning()) {
229                 Reference<?> list;
230                 try {
231                     synchronized (ReferenceQueue.class) {
232                         if (ReferenceQueue.unenqueued == null) {
233                             FinalizerWatchdogDaemon.INSTANCE.monitoringNotNeeded(
234                                     FinalizerWatchdogDaemon.RQ_DAEMON);
235                             // Increment after above call. If watchdog saw it active, it should see
236                             // the counter update.
237                             progressCounter.incrementAndGet();
238                             do {
239                                ReferenceQueue.class.wait();
240                             } while (ReferenceQueue.unenqueued == null);
241                             progressCounter.incrementAndGet();
242                             FinalizerWatchdogDaemon.INSTANCE.monitoringNeeded(
243                                     FinalizerWatchdogDaemon.RQ_DAEMON);
244                         }
245                         list = ReferenceQueue.unenqueued;
246                         ReferenceQueue.unenqueued = null;
247                     }
248                 } catch (InterruptedException e) {
249                     continue;
250                 } catch (OutOfMemoryError e) {
251                     continue;
252                 }
253                 ReferenceQueue.enqueuePending(list, progressCounter);
254                 FinalizerWatchdogDaemon.INSTANCE.resetTimeouts();
255             }
256         }
257 
currentlyProcessing()258         Object currentlyProcessing() {
259           return ReferenceQueue.getCurrentTarget();
260         }
261     }
262 
263     private static class FinalizerDaemon extends Daemon {
264         @UnsupportedAppUsage
265         private static final FinalizerDaemon INSTANCE = new FinalizerDaemon();
266         private final ReferenceQueue<Object> queue = FinalizerReference.queue;
267         private final AtomicInteger progressCounter = new AtomicInteger(0);
268         // Object (not reference!) being finalized. Accesses may race!
269         @UnsupportedAppUsage
270         private Object finalizingObject = null;
271 
FinalizerDaemon()272         FinalizerDaemon() {
273             super("FinalizerDaemon");
274         }
275 
runInternal()276         @Override public void runInternal() {
277             // This loop may be performance critical, since we need to keep up with mutator
278             // generation of finalizable objects.
279             // We minimize the amount of work we do per finalizable object. For example, we avoid
280             // reading the current time here, since that involves a kernel call per object.  We
281             // limit fast path communication with FinalizerWatchDogDaemon to what's unavoidable: A
282             // non-volatile store to communicate the current finalizable object, e.g. for
283             // reporting, and a release store (lazySet) to a counter.
284             // We do stop the  FinalizerWatchDogDaemon if we have nothing to do for a
285             // potentially extended period.  This prevents the device from waking up regularly
286             // during idle times.
287 
288             // Local copy of progressCounter; saves a fence per increment on ARM.
289             int localProgressCounter = progressCounter.get();
290 
291             FinalizerWatchdogDaemon.INSTANCE.monitoringNeeded(
292                     FinalizerWatchdogDaemon.FINALIZER_DAEMON);
293             while (isRunning()) {
294                 try {
295                     // Use non-blocking poll to avoid FinalizerWatchdogDaemon communication
296                     // when busy.
297                     Object nextReference = queue.poll();
298                     if (nextReference != null) {
299                         progressCounter.lazySet(++localProgressCounter);
300                         processReference(nextReference);
301                     } else {
302                         finalizingObject = null;
303                         // Slow path; block.
304                         FinalizerWatchdogDaemon.INSTANCE.monitoringNotNeeded(
305                                 FinalizerWatchdogDaemon.FINALIZER_DAEMON);
306                         // Increment after above call. If watchdog saw it active, it should see
307                         // the counter update.
308                         progressCounter.set(++localProgressCounter);
309                         nextReference = queue.remove();
310                         progressCounter.set(++localProgressCounter);
311                         FinalizerWatchdogDaemon.INSTANCE.monitoringNeeded(
312                                 FinalizerWatchdogDaemon.FINALIZER_DAEMON);
313                         processReference(nextReference);
314                     }
315                 } catch (InterruptedException ignored) {
316                 } catch (OutOfMemoryError ignored) {
317                 }
318             }
319         }
320 
processReference(Object ref)321         private void processReference(Object ref) {
322             if (ref instanceof FinalizerReference finalizingReference) {
323                 finalizingObject = finalizingReference.get();
324                 doFinalize(finalizingReference);
325             } else if (ref instanceof Cleaner.Cleanable cleanableReference) {
326                 finalizingObject = cleanableReference;
327                 doClean(cleanableReference);
328             } else {
329                 throw new AssertionError("Unknown class was placed into queue: " + ref);
330             }
331         }
332 
333         @FindBugsSuppressWarnings("FI_EXPLICIT_INVOCATION")
doFinalize(FinalizerReference<?> reference)334         private void doFinalize(FinalizerReference<?> reference) {
335             FinalizerReference.remove(reference);
336             Object object = reference.get();
337             reference.clear();
338             try {
339                 object.finalize();
340             } catch (Throwable ex) {
341                 // The RI silently swallows these, but Android has always logged.
342                 System.logE("Uncaught exception thrown by finalizer", ex);
343             } finally {
344                 // Done finalizing, stop holding the object as live.
345                 finalizingObject = null;
346             }
347         }
348 
doClean(Cleaner.Cleanable cleanable)349         private void doClean(Cleaner.Cleanable cleanable) {
350             try {
351                 cleanable.clean();
352             } catch (Throwable ex) {
353                 System.logE("Uncaught exception thrown by cleaner", ex);
354             } finally {
355                 // No need to hold cleaner object.
356                 finalizingObject = null;
357             }
358         }
359     }
360 
361     /**
362      * The watchdog exits the VM if either the FinalizerDaemon, or the ReferenceQueueDaemon
363      * gets stuck. We consider the finalizer to be stuck if it spends more than
364      * MAX_FINALIZATION_MILLIS on one instance. We consider ReferenceQueueDaemon to be
365      * potentially stuck if it spends more than MAX_FINALIZATION_MILLIS processing a single
366      * Cleaner or transferring objects into a single queue, but only report if this happens
367      * a few times in a row, to compensate for the fact that multiple Cleaners may be involved.
368      */
369     private static class FinalizerWatchdogDaemon extends Daemon {
370         // Single bit values to identify daemon to be watched.
371         static final int FINALIZER_DAEMON = 1;
372         static final int RQ_DAEMON = 2;
373 
374         @UnsupportedAppUsage
375         private static final FinalizerWatchdogDaemon INSTANCE = new FinalizerWatchdogDaemon();
376         private static final VarHandle VH_ACTION;
377         static {
378             try {
379                 VH_ACTION = MethodHandles
380                         .privateLookupIn(
381                                 CleanerImpl.PhantomCleanableRef.class, MethodHandles.lookup())
382                         .findVarHandle(
383                                 CleanerImpl.PhantomCleanableRef.class, "action", Runnable.class);
384             } catch (NoSuchFieldException | IllegalAccessException e) {
385                 throw new AssertionError("PhantomCleanableRef should have action field", e);
386             }
387         }
388 
389         private int activeWatchees;  // Only synchronized accesses.
390 
391         private long finalizerTimeoutNs = 0;  // Lazily initialized.
392 
393         // We tolerate this many timeouts during an enqueuePending call.
394         // This number is > 1, since we may only report enqueuePending progress rarely.
395         private static final int TOLERATED_REFERENCE_QUEUE_TIMEOUTS = 5;
396         private static final AtomicInteger observedReferenceQueueTimeouts = new AtomicInteger(0);
397 
FinalizerWatchdogDaemon()398         FinalizerWatchdogDaemon() {
399             super("FinalizerWatchdogDaemon");
400         }
401 
resetTimeouts()402         void resetTimeouts() {
403             observedReferenceQueueTimeouts.lazySet(0);
404         }
405 
runInternal()406         @Override public void runInternal() {
407             while (isRunning()) {
408                 if (!sleepUntilNeeded()) {
409                     // We have been interrupted, need to see if this daemon has been stopped.
410                     continue;
411                 }
412                 final TimeoutException exception = waitForProgress();
413                 if (exception != null && !VMDebug.isDebuggerConnected()) {
414                     timedOut(exception);
415                     break;
416                 }
417             }
418         }
419 
420         /**
421          * Wait until something is ready to be finalized.
422          * Return false if we have been interrupted
423          * See also http://code.google.com/p/android/issues/detail?id=22778.
424          */
sleepUntilNeeded()425         private synchronized boolean sleepUntilNeeded() {
426             while (activeWatchees == 0) {
427                 try {
428                     wait();
429                 } catch (InterruptedException e) {
430                     // Daemon.stop may have interrupted us.
431                     return false;
432                 } catch (OutOfMemoryError e) {
433                     return false;
434                 }
435             }
436             return true;
437         }
438 
439         /**
440          * Notify daemon that it's OK to sleep until notified that something is ready to be
441          * finalized.
442          */
monitoringNotNeeded(int whichDaemon)443         private synchronized void monitoringNotNeeded(int whichDaemon) {
444             activeWatchees &= ~whichDaemon;
445         }
446 
447         /**
448          * Notify daemon that there is something ready to be finalized.
449          */
monitoringNeeded(int whichDaemon)450         private synchronized void monitoringNeeded(int whichDaemon) {
451             int oldWatchees = activeWatchees;
452             activeWatchees |= whichDaemon;
453 
454             if (oldWatchees == 0) {
455                 notify();
456             }
457         }
458 
isActive(int whichDaemon)459         private synchronized boolean isActive(int whichDaemon) {
460             return (activeWatchees & whichDaemon) != 0;
461         }
462 
463         /**
464          * Sleep for the given number of nanoseconds, or slightly longer.
465          * @return false if we were interrupted.
466          */
sleepForNanos(long durationNanos)467         private boolean sleepForNanos(long durationNanos) {
468             // It's important to base this on nanoTime(), not currentTimeMillis(), since
469             // the former stops counting when the processor isn't running.
470             long startNanos = System.nanoTime();
471             while (true) {
472                 long elapsedNanos = System.nanoTime() - startNanos;
473                 long sleepNanos = durationNanos - elapsedNanos;
474                 if (sleepNanos <= 0) {
475                     return true;
476                 }
477                 // Ensure the nano time is always rounded up to the next whole millisecond,
478                 // ensuring the delay is >= the requested delay.
479                 long sleepMillis = (sleepNanos + NANOS_PER_MILLI - 1) / NANOS_PER_MILLI;
480                 try {
481                     Thread.sleep(sleepMillis);
482                 } catch (InterruptedException e) {
483                     if (!isRunning()) {
484                         return false;
485                     }
486                 } catch (OutOfMemoryError ignored) {
487                     if (!isRunning()) {
488                         return false;
489                     }
490                 }
491             }
492         }
493 
494 
495         /**
496          * Return null (normal case) or an exception describing what timed out.
497          * Wait VMRuntime.getFinalizerTimeoutMs.  If the FinalizerDaemon took essentially the
498          * whole time processing a single reference, or the ReferenceQueueDaemon failed to make
499          * visible progress during that time, return an exception.  Only called from a single
500          * thread.
501          */
waitForProgress()502         private TimeoutException waitForProgress() {
503             if (finalizerTimeoutNs == 0) {
504                 finalizerTimeoutNs =
505                         NANOS_PER_MILLI * VMRuntime.getRuntime().getFinalizerTimeoutMs();
506                 // Temporary app backward compatibility. Remove eventually.
507                 MAX_FINALIZE_NANOS = finalizerTimeoutNs;
508             }
509             // Read the counter before we read the "active" state the first time, and after
510             // we read it the last time, to guarantee that if the state was ever inactive,
511             // we'll see a changed counter.
512             int finalizerStartCount = FinalizerDaemon.INSTANCE.progressCounter.get();
513             boolean monitorFinalizer = isActive(FINALIZER_DAEMON);
514             int refQueueStartCount = ReferenceQueueDaemon.INSTANCE.progressCounter.get();
515             boolean monitorRefQueue = isActive(RQ_DAEMON);
516             // Avoid remembering object being finalized, so as not to keep it alive.
517             final long startMillis = System.currentTimeMillis();
518             final long startNanos = System.nanoTime();
519 
520             // Rather than just sleeping for finalizerTimeoutNs and checking whether we made
521             // progress, we sleep repeatedly. This means that if our process makes no progress,
522             // e.g. because it is frozen, the watchdog also won't, making it less likely we will
523             // spuriously time out. It does mean that in the normal case, we will go to sleep
524             // and wake up twice per timeout period, rather than once.
525             final int NUM_WAKEUPS = 5;
526             for (int i = 1; i <= NUM_WAKEUPS; ++i) {
527                 if (!sleepForNanos(finalizerTimeoutNs / NUM_WAKEUPS)) {
528                     // Don't report possibly spurious timeout if we are interrupted.
529                     return null;
530                 }
531                 if (monitorFinalizer && isActive(FINALIZER_DAEMON)
532                     && FinalizerDaemon.INSTANCE.progressCounter.get() == finalizerStartCount) {
533                     // Still working on same finalizer or Java 9 Cleaner.
534                     continue;
535                 }
536                 if (monitorRefQueue && isActive(RQ_DAEMON)
537                     && ReferenceQueueDaemon.INSTANCE.progressCounter.get() == refQueueStartCount) {
538                     // Still working on same ReferenceQueue or sun.misc.Cleaner.
539                     continue;
540                 }
541                 // Everything that could make progress, already did. Just sleep for the rest of the
542                 // timeout interval.
543                 if (i < NUM_WAKEUPS) {
544                     sleepForNanos((finalizerTimeoutNs  / NUM_WAKEUPS) * (NUM_WAKEUPS - i));
545                     return null;
546                 }
547             }
548             // Either a state change to inactive, or a task completion would have caused us to see a
549             // counter change. Thus at least one of the daemons appears stuck.
550             if (monitorFinalizer && isActive(FINALIZER_DAEMON)
551                 && FinalizerDaemon.INSTANCE.progressCounter.get() == finalizerStartCount) {
552                 // The finalizingObject field was set just before the counter increment, which
553                 // preceded the doFinalize() or doClean() call.  Thus we are guaranteed to get the
554                 // correct finalizing value below, unless doFinalize() just finished as we were
555                 // timing out, in which case we may get null or a later one.
556                 Object finalizing = FinalizerDaemon.INSTANCE.finalizingObject;
557                 System.logE("Was finalizing " + finalizingObjectAsString(finalizing)
558                     + ", now finalizing "
559                     + finalizingObjectAsString(FinalizerDaemon.INSTANCE.finalizingObject));
560                 // Print both time of day and monotonic time differences:
561                 System.logE("Total elapsed millis: "
562                     + (System.currentTimeMillis() - startMillis));
563                 System.logE("Total elapsed nanos: " + (System.nanoTime() - startNanos));
564                 return finalizerTimeoutException(finalizing);
565             }
566             if (monitorRefQueue && isActive(RQ_DAEMON)
567                 && ReferenceQueueDaemon.INSTANCE.progressCounter.get() == refQueueStartCount) {
568                 // Report RQD timeouts only if they occur repeatedly.
569                 // TODO: Consider changing that, but we have historically been more tolerant here,
570                 // since we may not increment the reference counter for every processed queue
571                 // element.
572                 String currentTarget = ReferenceQueueDaemon.INSTANCE.currentlyProcessing().toString();
573                 System.logE("ReferenceQueueDaemon timed out while targeting " + currentTarget
574                         + ". Total nanos: " + (System.nanoTime() - startNanos));
575                 if (observedReferenceQueueTimeouts.incrementAndGet()
576                         > TOLERATED_REFERENCE_QUEUE_TIMEOUTS) {
577                     return refQueueTimeoutException(currentTarget);
578                 }
579             }
580             return null;
581         }
582 
finalizerTimeoutException(Object object)583         private static TimeoutException finalizerTimeoutException(Object object) {
584             StringBuilder messageBuilder = new StringBuilder();
585 
586             if (object instanceof Cleaner.Cleanable) {
587                 messageBuilder.append(VH_ACTION.get(object).getClass().getName());
588             } else {
589                 messageBuilder.append(object.getClass().getName()).append(".finalize()");
590             }
591 
592             messageBuilder.append(" timed out after ")
593                     .append(VMRuntime.getRuntime().getFinalizerTimeoutMs() / 1000)
594                     .append(" seconds");
595             TimeoutException syntheticException = new TimeoutException(messageBuilder.toString());
596             // We use the stack from where finalize() was running to show where it was stuck.
597             syntheticException.setStackTrace(FinalizerDaemon.INSTANCE.getStackTrace());
598             return syntheticException;
599         }
600 
finalizingObjectAsString(Object obj)601         private static String finalizingObjectAsString(Object obj) {
602             if (obj instanceof Cleaner.Cleanable) {
603                 return VH_ACTION.get(obj).toString();
604             } else {
605                 return obj.toString();
606             }
607         }
608 
refQueueTimeoutException(String target)609         private static TimeoutException refQueueTimeoutException(String target) {
610             String message = "ReferenceQueueDaemon timed out while targeting " + target;
611             return new TimeoutException(message);
612         }
613 
timedOut(TimeoutException exception)614         private static void timedOut(TimeoutException exception) {
615             // Send SIGQUIT to get native stack traces.
616             try {
617                 Os.kill(Os.getpid(), OsConstants.SIGQUIT);
618                 // Sleep a few seconds to let the stack traces print.
619                 Thread.sleep(5000);
620             } catch (Exception e) {
621                 System.logE("failed to send SIGQUIT", e);
622             } catch (OutOfMemoryError ignored) {
623                 // May occur while trying to allocate the exception.
624             }
625 
626             // Ideally, we'd want to do this if this Thread had no handler to dispatch to.
627             // Unfortunately, it's extremely to messy to query whether a given Thread has *some*
628             // handler to dispatch to, either via a handler set on itself, via its ThreadGroup
629             // object or via the defaultUncaughtExceptionHandler.
630             //
631             // As an approximation, we log by hand and exit if there's no pre-exception handler nor
632             // a default uncaught exception handler.
633             //
634             // Note that this condition will only ever be hit by ART host tests and standalone
635             // dalvikvm invocations. All zygote forked process *will* have a pre-handler set
636             // in RuntimeInit and they cannot subsequently override it.
637             if (Thread.getUncaughtExceptionPreHandler() == null &&
638                     Thread.getDefaultUncaughtExceptionHandler() == null) {
639                 // If we have no handler, log and exit.
640                 System.logE(exception.getMessage(), exception);
641                 System.exit(2);
642             }
643 
644             // Otherwise call the handler to do crash reporting.
645             // We don't just throw because we're not the thread that
646             // timed out; we're the thread that detected it.
647             Thread.currentThread().dispatchUncaughtException(exception);
648         }
649     }
650 
651     // Adds a heap trim task to the heap event processor, not called from java. Left for
652     // compatibility purposes due to reflection.
653     @UnsupportedAppUsage
requestHeapTrim()654     public static void requestHeapTrim() {
655         VMRuntime.getRuntime().requestHeapTrim();
656     }
657 
658     // Adds a concurrent GC request task ot the heap event processor, not called from java. Left
659     // for compatibility purposes due to reflection.
requestGC()660     public static void requestGC() {
661         VMRuntime.getRuntime().requestConcurrentGC();
662     }
663 
664     private static class HeapTaskDaemon extends Daemon {
665         private static final HeapTaskDaemon INSTANCE = new HeapTaskDaemon();
666 
HeapTaskDaemon()667         HeapTaskDaemon() {
668             super("HeapTaskDaemon");
669         }
670 
671         // Overrides the Daemon.interupt method which is called from Daemons.stop.
interrupt(Thread thread)672         public synchronized void interrupt(Thread thread) {
673             VMRuntime.getRuntime().stopHeapTaskProcessor();
674         }
675 
runInternal()676         @Override public void runInternal() {
677             synchronized (this) {
678                 if (isRunning()) {
679                   // Needs to be synchronized or else we there is a race condition where we start
680                   // the thread, call stopHeapTaskProcessor before we start the heap task
681                   // processor, resulting in a deadlock since startHeapTaskProcessor restarts it
682                   // while the other thread is waiting in Daemons.stop().
683                   VMRuntime.getRuntime().startHeapTaskProcessor();
684                 }
685             }
686             // This runs tasks until we are stopped and there is no more pending task.
687             VMRuntime.getRuntime().runHeapTasks();
688         }
689     }
690 }
691