REVIEW 6511515: poor performance of LogRecord.inferCaller depending on java.lang.Throwable.getStackTraceElement
David Holmes - Sun Microsystems
David.Holmes at Sun.COM
Tue Apr 21 01:39:05 UTC 2009
Hi Martin, Jeremy,
I'm assuming inferCaller always expects to find a caller, or at least
that would be the common case - yes? If so then this might be a case
where it is quicker to forego the getStackTraceDepth() call and simply
catch the potential exception if we reach the end.
Aside: we really must get webrev or something like it available. These
simple diffs are really hard to evaluate in isolation.
Cheers,
David
Martin Buchholz said the following on 04/21/09 09:25:
> Hi java.util.logging maintainers,
>
> Here is a patch to improve the performance of
> LogRecord.inferCaller. This improves the performance
> of the microbenchmark in 6511515 by 5x.
> Swamy, please review.
> Contributed by Jeremy Manson.
> http://cr.openjdk.java.net/~martin/inferCaller
>
> As a followon, it makes sense to allow full random
> access to elements of a stack trace. If that is done,
> this change would be simplified by not requiring
> SharedSecrets.
>
> # HG changeset patch
> # User martin
> # Date 1240268799 25200
> # Node ID ba9c54ffcac48493a46ee902dae6809cf4678e8d
> # Parent 8b326aebb981265a99ed96355efe28f8c1b0a0c0
> 6511515: poor performance of LogRecord.inferCaller depending on
> java.lang.Throwable.getStackTraceElement
> Reviewed-by: swamyv
> Contributed-by: jeremymanson
> Summary: Allow random access to stack trace elements; retrieve only needed ones
>
> diff --git a/src/share/classes/java/lang/System.java
> b/src/share/classes/java/lang/System.java
> --- a/src/share/classes/java/lang/System.java
> +++ b/src/share/classes/java/lang/System.java
> @@ -1174,6 +1174,12 @@
> public void registerShutdownHook(int slot, Runnable r) {
> Shutdown.add(slot, r);
> }
> + public int getStackTraceDepth(Throwable t) {
> + return t.getStackTraceDepth();
> + }
> + public StackTraceElement getStackTraceElement(Throwable t, int i) {
> + return t.getStackTraceElement(i);
> + }
> });
> }
>
> diff --git a/src/share/classes/java/lang/Throwable.java
> b/src/share/classes/java/lang/Throwable.java
> --- a/src/share/classes/java/lang/Throwable.java
> +++ b/src/share/classes/java/lang/Throwable.java
> @@ -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 < 0 ||
> * index >= getStackTraceDepth() </tt>
> */
> - private native StackTraceElement getStackTraceElement(int index);
> + native StackTraceElement getStackTraceElement(int index);
>
> private synchronized void writeObject(java.io.ObjectOutputStream s)
> throws IOException
> diff --git a/src/share/classes/java/util/logging/LogRecord.java
> b/src/share/classes/java/util/logging/LogRecord.java
> --- a/src/share/classes/java/util/logging/LogRecord.java
> +++ b/src/share/classes/java/util/logging/LogRecord.java
> @@ -28,6 +28,9 @@
> import java.util.concurrent.atomic.AtomicInteger;
> 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
> @@ -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.
> diff --git a/src/share/classes/sun/misc/JavaLangAccess.java
> b/src/share/classes/sun/misc/JavaLangAccess.java
> --- a/src/share/classes/sun/misc/JavaLangAccess.java
> +++ b/src/share/classes/sun/misc/JavaLangAccess.java
> @@ -57,4 +57,14 @@
>
> /** register shutdown hook */
> void registerShutdownHook(int slot, Runnable r);
> +
> + /**
> + * 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);
> }
More information about the core-libs-dev
mailing list