I wasn't the one who ran the test, so I don't know for sure. My theory was that getCallerClass() returns a single frame, but the SecurityManager must allocate an array of appropriate size (which involves some overhead) and then return all of the frames. I chalked the difference up to that. My conclusion from the data was: If you need a whole stack, SecurityManager is clearly the best option. If you need a single frame, getCallerClass() is the only option that makes any sense.
On Mon, Jul 29, 2013 at 8:21 AM, David M. Lloyd <david.ll...@redhat.com> wrote: > I find it very interesting that reflection is no less than two orders of > magnitude faster than the security manager solution. How big was the stack > in these tests? It makes me wonder if maybe the implementation of the > security manager's getContext() method should be reevaluated a bit. > > > On 07/29/2013 07:53 AM, Nick Williams wrote: >> >> Just so that everyone understands how important this subject is, this >> change to getCallerClass(...) is being labeled a "disaster" for logging >> frameworks everywhere. Here's a benchmark for getting Classes from the >> following methods: >> >>> 1,000,000 calls of all alternatives were measured as follows : >>> Reflection: 10.195 ms. >>> Current Thread StackTrace: 5886.964 ms. >>> Throwable StackTrace: 4700.073 ms. >>> SecurityManager: 1046.804 ms. >> >> >> My goal here is to get the entire list engaged in coming up with the right >> solution. We (the community) can't afford for Java 8 not to have an >> equivalent replacement for getCallerClass(). >> >> Nick >> >> On Jul 27, 2013, at 2:01 PM, Nick Williams wrote: >> >>> All, >>> >>> In the last two months, there have been a number of discussions >>> surrounding stack traces, Classes on the stack trace, and caller classes >>> [1], [2], [3]. These are all related discussions and the solution to them is >>> equally related, so I wanted to consolidate it all into this one discussion >>> where I hope we can finalize on a solution and get it implemented for Java >>> 8. >>> >>> In a nut shell, here are the underlying needs that I have seen expressed >>> through many, many messages: >>> >>> - Some code needs to get the Class of the caller of the current method, >>> skipping any reflection methods. >>> - Some code needs to get the Class of the caller /n/ stack frames before >>> the current method, skipping any reflection methods. >>> - Some code needs to get the current stack trace, populated with Classes, >>> Executables, file names, line numbers, and native flags instead of the >>> String class names and String method names in StackTraceElement. This >>> /should/ include any reflection methods, just like StackTraceElement[]s. >>> - Some code needs to get the stack trace from when a Throwable was >>> created, populated with Classes, Executables, file names, line numbers, and >>> native flags instead of the String class names and String method names in >>> StackTraceElement. This /should/ include any reflection methods, just like >>> StackTraceElement[]s. >>> - There needs to be a reflection way to achieve all of this since some >>> libraries (e.g., Log4j) need to be compiled against Java 6 but run on 7 and >>> 8 (and thus can't use @CallerSensitive). >>> >>> I believe the solutions to these needs are all related. Importantly, I >>> think it is very important that action be taken in Java 8 due to the changes >>> made to sun.reflect.Reflection#getCallerClass(...). While we all understand >>> that relying on private sun.* APIs is not safe, the fact is that many people >>> have relied on sun.reflect.Reflection#getCallerClass(...) due to the fact >>> that there is simply no other way to do this in the standard API. This >>> includes Log4j 2, Logback, SLF4j, and Groovy, some features of which will >>> stop working correctly in Java 7 >= u25. >>> >>> I would point out that this could all easily be solved simply by adding a >>> getElementClass() method to StackTraceElement, but there was strong >>> opposition to this, largely due to serialization issues. Since that is >>> apparently not an option, I propose the following API, based on the various >>> discussions in the last two months, StackTraceElement, and the API that .NET >>> provides to achieve the same needs as listed above: >>> >>> CallerSensitive.java: >>> package java.lang; >>> >>> /** Previously private API, now public */ >>> public @interface CallerSensitive { >>> ... >>> } >>> >>> StackTraceFrame.java: >>> package java.lang; >>> >>> import java.util.Objects. >>> >>> public final class StackTraceFrame { >>> private final Class<?> declaringClass; >>> private final Executable executable; >>> private final String fileName; >>> private final int lineNumber; >>> >>> public StackTraceFrame(Class<?> declaringClass, Executable >>> executable, String fileName, int lineNumber) { >>> this.declaringClass = Objects.requireNonNull(declaringClass, >>> "Declaring class is null"); >>> this.executable = Objects.requireNonNull(executable, "Executable >>> is null"); >>> this.fileName = fileName; >>> this.lineNumber = lineNumber; >>> } >>> >>> public Class<?> getDeclaringClass() { >>> return this.declaringClass; >>> } >>> >>> public Executable getExecutable() { >>> return this.executable; >>> } >>> >>> public String getFileName() { >>> return this.fileName; >>> } >>> >>> public int getLineNumber() { >>> return this.lineNumber; >>> } >>> >>> public boolean isNative() { >>> return this.lineNumber == -2; >>> } >>> >>> public String toString() { /* Same as StackTraceElement */ } >>> public boolean equals() { /* Ditto */ } >>> public int hashCode() { /* Ditto */ } >>> >>> /** Uses @CallerSensitive */ >>> public static native StackTraceFrame getCallerFrame(); >>> >>> /** Works like Java < 7u25 sun.reflect.Reflection#getCallerClass() >>> */ >>> public static native StackTraceFrame getCallerFrame(int skipFrames); >>> >>> public static native StackTraceFrame[] getCurrentStackTrace(); >>> } >>> >>> Throwable.java: >>> package java.lang; >>> >>> ... >>> >>> public class Throwable { >>> ... >>> public synchronized Throwable fillInStackTraceFrames() { ... } >>> >>> private native Throwable fillInStackTraceFrames(int dummy); >>> >>> public StackTraceFrame[] getStackTraceFrames() { >>> return this.getOurStackTraceFrames().clone(); >>> } >>> >>> private synchronized StackTraceFrame[] getOurStackTraceFrames() { >>> ... } >>> ... >>> } >>> >>> Furthermore, I propose that we restore the behavior of >>> sun.reflect.Reflection#getCallerClass(int) /just for Java 7/ since the >>> proposed above solution cannot be added to Java 7. >>> >>> I would love if we could quickly coalesce around this solution or a >>> derivative thereof so that it can be implemented before Feature Complete. >>> The absence of any replacement or alternative for >>> sun.reflect.Reflection#getCallerClass(int) will be a serious issue in Java 8 >>> that will cause hardships for many projects. >>> >>> [1] >>> http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-June/018049.html >>> [2] >>> http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-June/018349.html, >>> http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-July/019098.html >>> [3] >>> http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-July/018855.html >> >> > > > -- > - DML