From 599c918d9794b51992de85b42befa0c71d9ec07f Mon Sep 17 00:00:00 2001 From: Andy McFadden <> Date: Wed, 8 Apr 2009 00:35:56 -0700 Subject: [PATCH] AI 144931: Added a (hidden) way to "pre-cache" register maps. The 50 methods that appeared on the GC stacks of the most applications require 13KB of native heap for their uncompressed register maps, and the full set took 5ms to uncompress. Pre-computation doesn't represent a significant improvement in space or time, at the cost of a big pile of strings in ZygoteInit. I'm leaving the method in ZygoteInit, but it's not called, and the static final String[] of method descriptors is empty. We may want to revisit this later. BUG=1729570 Automated import of CL 144931 --- core/java/android/os/Debug.java | 19 ++++++ .../com/android/internal/os/ZygoteInit.java | 61 ++++++++++++++++++- 2 files changed, 79 insertions(+), 1 deletion(-) 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());