diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 5ea5aaedccaf6..e73a97e0b04cc 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -632,6 +632,25 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo */ public static final native int getBinderDeathObjectCount(); + /** + * Primes the register map cache. + * + * Only works for classes in the bootstrap class loader. Does not + * cause classes to be loaded if they're not already present. + * + * The classAndMethodDesc argument is a concatentation of the VM-internal + * class descriptor, method name, and method descriptor. Examples: + * Landroid/os/Looper;.loop:()V + * Landroid/app/ActivityThread;.main:([Ljava/lang/String;)V + * + * @param classAndMethodDesc the method to prepare + * + * @hide + */ + public static final boolean cacheRegisterMap(String classAndMethodDesc) { + return VMDebug.cacheRegisterMap(classAndMethodDesc); + } + /** * API for gathering and querying instruction counts. * diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index ac8b589341684..fa2bb22b5cbbe 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -93,7 +93,26 @@ public class ZygoteInit { /** Controls whether we should preload resources during zygote init. */ private static final boolean PRELOAD_RESOURCES = true; - + + /** + * List of methods we "warm up" in the register map cache. These were + * chosen because they appeared on the stack in GCs in multiple + * applications. + * + * This is in a VM-ready format, to minimize string processing. If a + * class is not already loaded, or a method is not found, the entry + * will be skipped. + * + * This doesn't really merit a separately-generated input file at this + * time. The list is fairly short, and the consequences of failure + * are minor. + */ + private static final String[] REGISTER_MAP_METHODS = { + // (currently not doing any) + //"Landroid/app/Activity;.setContentView:(I)V", + }; + + /** * Invokes a static "main(argv[]) method on class "className". * Converts various failing exceptions into RuntimeExceptions, with @@ -318,6 +337,45 @@ public class ZygoteInit { } } + /** + * Pre-caches register maps for methods that are commonly used. + */ + private static void cacheRegisterMaps() { + String failed = null; + int failure; + long startTime = System.nanoTime(); + + failure = 0; + + for (int i = 0; i < REGISTER_MAP_METHODS.length; i++) { + String str = REGISTER_MAP_METHODS[i]; + + if (!Debug.cacheRegisterMap(str)) { + if (failed == null) + failed = str; + failure++; + } + } + + long delta = System.nanoTime() - startTime; + + if (failure == REGISTER_MAP_METHODS.length) { + if (REGISTER_MAP_METHODS.length > 0) { + Log.i(TAG, + "Register map caching failed (precise GC not enabled?)"); + } + return; + } + + Log.i(TAG, "Register map cache: found " + + (REGISTER_MAP_METHODS.length - failure) + " of " + + REGISTER_MAP_METHODS.length + " methods in " + + (delta / 1000000L) + "ms"); + if (failure > 0) { + Log.i(TAG, " First failure: " + failed); + } + } + /** * Load in commonly used resources, so they can be shared across * processes. @@ -510,6 +568,7 @@ public class ZygoteInit { EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START, SystemClock.uptimeMillis()); preloadClasses(); + //cacheRegisterMaps(); preloadResources(); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END, SystemClock.uptimeMillis());