Skip to content

Instantly share code, notes, and snippets.

@alblue
Created October 4, 2015 14:04
Show Gist options
  • Save alblue/c97a2c85d6007c992917 to your computer and use it in GitHub Desktop.
Save alblue/c97a2c85d6007c992917 to your computer and use it in GitHub Desktop.

Running this with:

$ java -XX:+TraceClassLoading Main

or

$ java -XX:+TraceClassLoading -Dmethodref=true Main 

shows that there is a difference between using a method reference and a method reflection when calling a method.

In the case of method reflection, getDeclaredMethod(name) is called, which calls getDeclaredMethods() and post-filters them to find the one with the desired name. This has the side-effect of loading an argument type for a completely unrelated method in the same class; in this case, Target::unsued(Unused) which is not called anywhere in this program.

The fact that we're trying to look up Target.class.getDeclaredMethod("reflect") is enough to have a side-effect of loading the bytes for the Unused argument type, even if no class initializers actually occur. As a result, using reflection to call methods can trigger disk I/O and (potentially) large amounts of unneed code into an application. Although not shown here, if Unused had super types (or super-interfaces) then these too would be recursively loaded.

package foo;
import java.lang.reflect.Method;
public class Main {
public static boolean TEST_REFLECTION = false;
public static void main(String[] args) throws Exception {
System.out.println("Instantiating Target");
Target target = new Target();
System.out.println("About to call target.direct()");
target.direct();
if (TEST_REFLECTION) {
System.out.println("About to call target.reflect()");
Method reflect = Target.class.getDeclaredMethod("reflect");
reflect.invoke(target);
} else {
System.out.println("About to call target::reflect via Runnable");
Runnable r = target::reflect;
r.run();
}
}
}
$ java -XX:+TraceClassLoading -Dmethodref=true Main
...
[Loaded foo.Main from file:/tmp/]
[Loaded sun.launcher.LauncherHelper$FXHelper from /Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Class$MethodArray from /Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Void from /Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/rt.jar]
Instantiating Target
[Loaded foo.Target from file:/tmp/]
Calling Target::<clinit>
About to call target.direct()
Calling Target::direct
About to call target::reflect via Runnable
[Loaded java.lang.invoke.LambdaMetafactory from /Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/rt.jar]
... // Lots of lambda stuff, but no foo.Unsued
[Loaded java.lang.invoke.LambdaForm$MH/1418481495 from java.lang.invoke.LambdaForm]
Calling Target::reflect via reflection
[Loaded java.lang.Shutdown from /Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Shutdown$Lock from /Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/rt.jar]
$ java -XX:+TraceClassLoading Main
...
[Loaded foo.Main from file:/tmp/]
[Loaded sun.launcher.LauncherHelper$FXHelper from /Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Class$MethodArray from /Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Void from /Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/rt.jar]
Instantiating Target
[Loaded foo.Target from file:/tmp/]
Calling Target::<clinit>
About to call target.direct()
Calling Target::direct
About to call target.reflect()
[Loaded foo.Unused from file:/tmp/] <- we are loading, but not initializing, the argument type of an unrelated method here
[Loaded sun.reflect.NativeMethodAccessorImpl from /Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded sun.reflect.DelegatingMethodAccessorImpl from /Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/rt.jar]
Calling Target::reflect via reflection
[Loaded java.lang.Shutdown from /Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Shutdown$Lock from /Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/rt.jar]
package foo;
public class Target {
static {
System.out.println("Calling Target::<clinit>");
}
public void direct() {
System.out.println("Calling Target::direct");
}
public void reflect() {
System.out.println("Calling Target::reflect via reflection");
}
public void unused(Unused unused) {
System.out.println("Caling Target::unused");
}
}
package foo;
public class Unused {
static {
System.out.println("Running Unused::<clinit>");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment