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