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