6511515: poor performance of LogRecord.inferCaller depending on java.lang.Throwable.getStackTraceElement
authormartin
Sun, 14 Jun 2009 14:33:30 -0700
changeset 2947 b0135c99348e
parent 2946 f95752c3204a
child 2948 7f4d7737d38f
child 3045 cdfc2f549bbc
6511515: poor performance of LogRecord.inferCaller depending on java.lang.Throwable.getStackTraceElement Summary: Allow random access to stack trace elements; retrieve only needed ones Reviewed-by: swamyv Contributed-by: jeremymanson@google.com
jdk/src/share/classes/java/lang/System.java
jdk/src/share/classes/java/lang/Throwable.java
jdk/src/share/classes/java/util/logging/LogRecord.java
jdk/src/share/classes/sun/misc/JavaLangAccess.java
--- a/jdk/src/share/classes/java/lang/System.java	Sun Jun 14 14:23:22 2009 -0700
+++ b/jdk/src/share/classes/java/lang/System.java	Sun Jun 14 14:33:30 2009 -0700
@@ -1174,6 +1174,12 @@
             public void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook) {
                 Shutdown.add(slot, registerShutdownInProgress, hook);
             }
+            public int getStackTraceDepth(Throwable t) {
+                return t.getStackTraceDepth();
+            }
+            public StackTraceElement getStackTraceElement(Throwable t, int i) {
+                return t.getStackTraceElement(i);
+            }
         });
     }
 
--- a/jdk/src/share/classes/java/lang/Throwable.java	Sun Jun 14 14:23:22 2009 -0700
+++ b/jdk/src/share/classes/java/lang/Throwable.java	Sun Jun 14 14:33:30 2009 -0700
@@ -645,17 +645,21 @@
     /**
      * Returns the number of elements in the stack trace (or 0 if the stack
      * trace is unavailable).
+     *
+     * package-protection for use by SharedSecrets.
      */
-    private native int getStackTraceDepth();
+    native int getStackTraceDepth();
 
     /**
      * Returns the specified element of the stack trace.
      *
+     * package-protection for use by SharedSecrets.
+     *
      * @param index index of the element to return.
      * @throws IndexOutOfBoundsException if <tt>index &lt; 0 ||
      *         index &gt;= getStackTraceDepth() </tt>
      */
-    private native StackTraceElement getStackTraceElement(int index);
+    native StackTraceElement getStackTraceElement(int index);
 
     private synchronized void writeObject(java.io.ObjectOutputStream s)
         throws IOException
--- a/jdk/src/share/classes/java/util/logging/LogRecord.java	Sun Jun 14 14:23:22 2009 -0700
+++ b/jdk/src/share/classes/java/util/logging/LogRecord.java	Sun Jun 14 14:33:30 2009 -0700
@@ -29,6 +29,9 @@
 import java.util.concurrent.atomic.AtomicLong;
 import java.io.*;
 
+import sun.misc.JavaLangAccess;
+import sun.misc.SharedSecrets;
+
 /**
  * LogRecord objects are used to pass logging requests between
  * the logging framework and individual log Handlers.
@@ -522,29 +525,31 @@
     // Private method to infer the caller's class and method names
     private void inferCaller() {
         needToInferCaller = false;
-        // Get the stack trace.
-        StackTraceElement stack[] = (new Throwable()).getStackTrace();
-        // First, search back to a method in the Logger class.
-        int ix = 0;
-        while (ix < stack.length) {
-            StackTraceElement frame = stack[ix];
+        JavaLangAccess access = SharedSecrets.getJavaLangAccess();
+        Throwable throwable = new Throwable();
+        int depth = access.getStackTraceDepth(throwable);
+
+        String logClassName = "java.util.logging.Logger";
+        boolean lookingForLogger = true;
+        for (int ix = 0; ix < depth; ix++) {
+            // Calling getStackTraceElement directly prevents the VM
+            // from paying the cost of building the entire stack frame.
+            StackTraceElement frame =
+                access.getStackTraceElement(throwable, ix);
             String cname = frame.getClassName();
-            if (cname.equals("java.util.logging.Logger")) {
-                break;
+            if (lookingForLogger) {
+                // Skip all frames until we have found the first logger frame.
+                if (cname.equals(logClassName)) {
+                    lookingForLogger = false;
+                }
+            } else {
+                if (!cname.equals(logClassName)) {
+                    // We've found the relevant frame.
+                    setSourceClassName(cname);
+                    setSourceMethodName(frame.getMethodName());
+                    return;
+                }
             }
-            ix++;
-        }
-        // Now search for the first frame before the "Logger" class.
-        while (ix < stack.length) {
-            StackTraceElement frame = stack[ix];
-            String cname = frame.getClassName();
-            if (!cname.equals("java.util.logging.Logger")) {
-                // We've found the relevant frame.
-                setSourceClassName(cname);
-                setSourceMethodName(frame.getMethodName());
-                return;
-            }
-            ix++;
         }
         // We haven't found a suitable frame, so just punt.  This is
         // OK as we are only committed to making a "best effort" here.
--- a/jdk/src/share/classes/sun/misc/JavaLangAccess.java	Sun Jun 14 14:23:22 2009 -0700
+++ b/jdk/src/share/classes/sun/misc/JavaLangAccess.java	Sun Jun 14 14:33:30 2009 -0700
@@ -73,4 +73,14 @@
      *          the slot is not valid to register.
      */
     void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook);
+
+    /**
+     * Returns the number of stack frames represented by the given throwable.
+     */
+    int getStackTraceDepth(Throwable t);
+
+    /**
+     * Returns the ith StackTraceElement for the given throwable.
+     */
+    StackTraceElement getStackTraceElement(Throwable t, int i);
 }