In the Java API there are a number of APIs that rely on being able to walk up the call stack to determine certain properties of the immediate caller of the API. The most common scenario is determining the caller's class or class loader. This information is used to implement security checks or to use the right class loader.
Up until now IKVM used the System.Diagnostics.StackFrame
.NET API to walk the stack. There are however two major issues with this API. The first issue is that it is unreliable and IKVM jumps through various hoops to make sure that the CLR doesn't inline methods it shouldn't inline or use tail call optimizations it shouldn't use. The second issue is that it is relatively slow.
I finally got around to implementing an alternative scheme that eliminates the usage of System.Diagnostics.StackFrame
in most cases. In part 2 I'll describe the implementation, but for now let's just look at some performance numbers.
I've cooked up two microbenchmarks that clearly demonstrate the difference between the current approach and the new approach.
Class.forName()
In this microbenchmark we look at the Class.forName()
API. It walks the call stack to determine the class loader of the immediate caller, because that is the class loader that it needs to use to load the class.
class ForName {
public static void main(String[] args) throws Exception {
Class.forName("java.lang.Object");
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++)
Class.forName("java.lang.Object");
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
ForName results:
|
x86 |
x64 |
IKVM 0.37.3063 |
4350 |
3959 |
IKVM 0.37.3073 |
45 |
49 |
JDK 1.6 |
138 |
97 |
(The difference between JDK 1.6 x86 and x64 is mostly due to x86 defaulting to HotSpot Client and x64 to HotSpot Server.)
Method.invoke()
The Method.invoke()
API uses the caller class to perform the required access checks when you are calling a non-public method or a method in non-public class.
class Invoke {
public static void main(String[] args) throws Exception {
java.lang.reflect.Method m = Invoke.class.getDeclaredMethod("foo");
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++)
m.invoke(null);
long end = System.currentTimeMillis();
System.out.println(end - start);
}
private static void foo() { }
}
Invoke results:
|
x86 |
x64 |
IKVM 0.37.3063 |
6083 |
5572 |
IKVM 0.37.3073 |
14 |
16 |
JDK 1.6 |
82 |
39 |
To be continued in part 2...