Daemons.java revision 152be540e335376f66ed662d8e63e601a09cb4b3
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 17package java.lang; 18 19import dalvik.system.VMRuntime; 20import java.lang.ref.FinalizerReference; 21import java.lang.ref.Reference; 22import java.lang.ref.ReferenceQueue; 23import java.util.concurrent.atomic.AtomicBoolean; 24import java.util.concurrent.TimeoutException; 25import libcore.util.EmptyArray; 26 27/** 28 * Calls Object.finalize() on objects in the finalizer reference queue. The VM 29 * will abort if any finalize() call takes more than the maximum finalize time 30 * to complete. 31 * 32 * @hide 33 */ 34public final class Daemons { 35 private static final int NANOS_PER_MILLI = 1000 * 1000; 36 private static final int NANOS_PER_SECOND = NANOS_PER_MILLI * 1000; 37 private static final long MAX_FINALIZE_NANOS = 10L * NANOS_PER_SECOND; 38 39 public static void start() { 40 ReferenceQueueDaemon.INSTANCE.start(); 41 FinalizerDaemon.INSTANCE.start(); 42 FinalizerWatchdogDaemon.INSTANCE.start(); 43 HeapTaskDaemon.INSTANCE.start(); 44 } 45 46 public static void stop() { 47 HeapTaskDaemon.INSTANCE.stop(); 48 ReferenceQueueDaemon.INSTANCE.stop(); 49 FinalizerDaemon.INSTANCE.stop(); 50 FinalizerWatchdogDaemon.INSTANCE.stop(); 51 } 52 53 /** 54 * A background task that provides runtime support to the application. 55 * Daemons can be stopped and started, but only so that the zygote can be a 56 * single-threaded process when it forks. 57 */ 58 private static abstract class Daemon implements Runnable { 59 private Thread thread; 60 61 public synchronized void start() { 62 if (thread != null) { 63 throw new IllegalStateException("already running"); 64 } 65 thread = new Thread(ThreadGroup.systemThreadGroup, this, getClass().getSimpleName()); 66 thread.setDaemon(true); 67 thread.start(); 68 } 69 70 public abstract void run(); 71 72 /** 73 * Returns true while the current thread should continue to run; false 74 * when it should return. 75 */ 76 protected synchronized boolean isRunning() { 77 return thread != null; 78 } 79 80 public synchronized void interrupt() { 81 interrupt(thread); 82 } 83 84 public synchronized void interrupt(Thread thread) { 85 if (thread == null) { 86 throw new IllegalStateException("not running"); 87 } 88 thread.interrupt(); 89 } 90 91 /** 92 * Waits for the runtime thread to stop. This interrupts the thread 93 * currently running the runnable and then waits for it to exit. 94 */ 95 public void stop() { 96 Thread threadToStop; 97 synchronized (this) { 98 threadToStop = thread; 99 thread = null; 100 } 101 if (threadToStop == null) { 102 throw new IllegalStateException("not running"); 103 } 104 interrupt(threadToStop); 105 while (true) { 106 try { 107 threadToStop.join(); 108 return; 109 } catch (InterruptedException ignored) { 110 } 111 } 112 } 113 114 /** 115 * Returns the current stack trace of the thread, or an empty stack trace 116 * if the thread is not currently running. 117 */ 118 public synchronized StackTraceElement[] getStackTrace() { 119 return thread != null ? thread.getStackTrace() : EmptyArray.STACK_TRACE_ELEMENT; 120 } 121 } 122 123 /** 124 * This heap management thread moves elements from the garbage collector's 125 * pending list to the managed reference queue. 126 */ 127 private static class ReferenceQueueDaemon extends Daemon { 128 private static final ReferenceQueueDaemon INSTANCE = new ReferenceQueueDaemon(); 129 130 @Override public void run() { 131 while (isRunning()) { 132 Reference<?> list; 133 try { 134 synchronized (ReferenceQueue.class) { 135 while (ReferenceQueue.unenqueued == null) { 136 ReferenceQueue.class.wait(); 137 } 138 list = ReferenceQueue.unenqueued; 139 ReferenceQueue.unenqueued = null; 140 } 141 } catch (InterruptedException e) { 142 continue; 143 } 144 enqueue(list); 145 } 146 } 147 148 private void enqueue(Reference<?> list) { 149 Reference<?> start = list; 150 do { 151 // pendingNext is owned by the GC so no synchronization is required. 152 Reference<?> next = list.pendingNext; 153 list.pendingNext = null; 154 list.enqueueInternal(); 155 list = next; 156 } while (list != start); 157 } 158 } 159 160 private static class FinalizerDaemon extends Daemon { 161 private static final FinalizerDaemon INSTANCE = new FinalizerDaemon(); 162 private final ReferenceQueue<Object> queue = FinalizerReference.queue; 163 private volatile Object finalizingObject; 164 private volatile long finalizingStartedNanos; 165 166 @Override public void run() { 167 while (isRunning()) { 168 // Take a reference, blocking until one is ready or the thread should stop 169 try { 170 doFinalize((FinalizerReference<?>) queue.remove()); 171 } catch (InterruptedException ignored) { 172 } 173 } 174 } 175 176 @FindBugsSuppressWarnings("FI_EXPLICIT_INVOCATION") 177 private void doFinalize(FinalizerReference<?> reference) { 178 FinalizerReference.remove(reference); 179 Object object = reference.get(); 180 reference.clear(); 181 try { 182 finalizingStartedNanos = System.nanoTime(); 183 finalizingObject = object; 184 synchronized (FinalizerWatchdogDaemon.INSTANCE) { 185 FinalizerWatchdogDaemon.INSTANCE.notify(); 186 } 187 object.finalize(); 188 } catch (Throwable ex) { 189 // The RI silently swallows these, but Android has always logged. 190 System.logE("Uncaught exception thrown by finalizer", ex); 191 } finally { 192 // Done finalizing, stop holding the object as live. 193 finalizingObject = null; 194 } 195 } 196 } 197 198 /** 199 * The watchdog exits the VM if the finalizer ever gets stuck. We consider 200 * the finalizer to be stuck if it spends more than MAX_FINALIZATION_MILLIS 201 * on one instance. 202 */ 203 private static class FinalizerWatchdogDaemon extends Daemon { 204 private static final FinalizerWatchdogDaemon INSTANCE = new FinalizerWatchdogDaemon(); 205 206 @Override public void run() { 207 while (isRunning()) { 208 boolean waitSuccessful = waitForObject(); 209 if (waitSuccessful == false) { 210 // We have been interrupted, need to see if this daemon has been stopped. 211 continue; 212 } 213 boolean finalized = waitForFinalization(); 214 if (!finalized && !VMRuntime.getRuntime().isDebuggerActive()) { 215 Object finalizedObject = FinalizerDaemon.INSTANCE.finalizingObject; 216 // At this point we probably timed out, look at the object in case the finalize 217 // just finished. 218 if (finalizedObject != null) { 219 finalizerTimedOut(finalizedObject); 220 break; 221 } 222 } 223 } 224 } 225 226 private boolean waitForObject() { 227 while (true) { 228 Object object = FinalizerDaemon.INSTANCE.finalizingObject; 229 if (object != null) { 230 return true; 231 } 232 synchronized (this) { 233 // wait until something is ready to be finalized 234 // http://code.google.com/p/android/issues/detail?id=22778 235 try { 236 wait(); 237 } catch (InterruptedException e) { 238 // Daemon.stop may have interrupted us. 239 return false; 240 } 241 } 242 } 243 } 244 245 private void sleepFor(long startNanos, long durationNanos) { 246 while (true) { 247 long elapsedNanos = System.nanoTime() - startNanos; 248 long sleepNanos = durationNanos - elapsedNanos; 249 long sleepMills = sleepNanos / NANOS_PER_MILLI; 250 if (sleepMills <= 0) { 251 return; 252 } 253 try { 254 Thread.sleep(sleepMills); 255 } catch (InterruptedException e) { 256 if (!isRunning()) { 257 return; 258 } 259 } 260 } 261 } 262 263 private boolean waitForFinalization() { 264 long startTime = FinalizerDaemon.INSTANCE.finalizingStartedNanos; 265 sleepFor(startTime, MAX_FINALIZE_NANOS); 266 // If we are finalizing an object and the start time is the same, it must be that we 267 // timed out finalizing something. It may not be the same object that we started out 268 // with but this doesn't matter. 269 return FinalizerDaemon.INSTANCE.finalizingObject == null || 270 FinalizerDaemon.INSTANCE.finalizingStartedNanos != startTime; 271 } 272 273 private static void finalizerTimedOut(Object object) { 274 // The current object has exceeded the finalization deadline; abort! 275 String message = object.getClass().getName() + ".finalize() timed out after " 276 + (MAX_FINALIZE_NANOS / NANOS_PER_SECOND) + " seconds"; 277 Exception syntheticException = new TimeoutException(message); 278 // We use the stack from where finalize() was running to show where it was stuck. 279 syntheticException.setStackTrace(FinalizerDaemon.INSTANCE.getStackTrace()); 280 Thread.UncaughtExceptionHandler h = Thread.getDefaultUncaughtExceptionHandler(); 281 if (h == null) { 282 // If we have no handler, log and exit. 283 System.logE(message, syntheticException); 284 System.exit(2); 285 } 286 // Otherwise call the handler to do crash reporting. 287 // We don't just throw because we're not the thread that 288 // timed out; we're the thread that detected it. 289 h.uncaughtException(Thread.currentThread(), syntheticException); 290 } 291 } 292 293 // Adds a heap trim task ot the heap event processor, not called from java. Left for 294 // compatibility purposes due to reflection. 295 public static void requestHeapTrim() { 296 VMRuntime.getRuntime().requestHeapTrim(); 297 } 298 299 // Adds a concurrent GC request task ot the heap event processor, not called from java. Left 300 // for compatibility purposes due to reflection. 301 public static void requestGC() { 302 VMRuntime.getRuntime().requestConcurrentGC(); 303 } 304 305 private static class HeapTaskDaemon extends Daemon { 306 private static final HeapTaskDaemon INSTANCE = new HeapTaskDaemon(); 307 308 // Overrides the Daemon.interupt method which is called from Daemons.stop. 309 public synchronized void interrupt(Thread thread) { 310 VMRuntime.getRuntime().stopHeapTaskProcessor(); 311 } 312 313 @Override public void run() { 314 synchronized (this) { 315 if (isRunning()) { 316 // Needs to be synchronized or else we there is a race condition where we start 317 // the thread, call stopHeapTaskProcessor before we start the heap task 318 // processor, resulting in a deadlock since startHeapTaskProcessor restarts it 319 // while the other thread is waiting in Daemons.stop(). 320 VMRuntime.getRuntime().startHeapTaskProcessor(); 321 } 322 } 323 // This runs tasks until we are stopped and there is no more pending task. 324 VMRuntime.getRuntime().runHeapTasks(); 325 } 326 } 327} 328