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