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.