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