I was reading through the PerfMark code and saw a comment about avoid an accidental class load through using reflection in a commit:
if (Boolean.getBoolean("io.perfmark.PerfMark.debug")) {
- Logger.getLogger(PerfMark.class.getName()).log(Level.FINE, "Error during PerfMark.<clinit>", err);
+ // We need to be careful here, as it's easy to accidentally cause a class load. Logger is loaded
+ // reflectively to avoid accidentally pulling it in.
+ // TODO(carl-mastrangelo): Maybe make this load SLF4J instead?
+ Class<?> logClass = Class.forName("java.util.logging.Logger");
+ Object logger = logClass.getMethod("getLogger", String.class).invoke(null, PerfMark.class.getName());
..
}
I don't quite understand which class is prevented from being accidentally loaded here. According to Class#forName will cause the logger class to be loaded. From my understanding, the class will only be loaded if the enclosing if condition is true. Or is this the point I am missing?
Commit with more context is here: https://github.com/perfmark/perfmark/commit/4f87fb72c2077df6ade958b524d6d217766c9f93#diff-f9fdc8ad347ee9aa7a11a5259d5ab41c81e84c0ff375de17faebe7625cf50fb5R116
I ran the part with the if block and set a breakpoint on static and non-static fields in the Logger class. It hit the breakpoint only when the call was executed irregardless of using reflection or direct. When the if condition was false, no logger was loaded in any case.
I think the important point of that commit is to load the classes from
java.util.logging
only when it is really required (when the system property "io.perfmark.PerfMark.debug" is "true" anderr
is notnull
, i.e. when the classio.perfmark.impl.SecretPerfMarkImpl$PerfMarkImpl
is not available or that class has not the required constructor.)If the code is
then the
java.util.logging.Logger
class may be loaded as soon as thePerfMark
class is verified and linked (since linkingPerfMark
requires that the static initializer block is executed).With this convoluted code the
java.util.logging.Logger
is only loaded ifPerfMark
cannot load its support classio.perfmark.impl.SecretPerfMarkImpl$PerfMarkImpl
and the system property "io.perfmark.PerfMark.debug" is set to "true" (which probably means thatjava.util.logging.Logger
is almost never loaded just because you usePerfMark
)The JVM Specification has clauses that loading / verifying / linking of a class is not required to load all the referenced classes, and modern JVM implementations will probably implement many of these points to reduce unnecessary class loading and improve performance. But keep in mind that
PerfMark
as a very generic library that supports Java versions from 1.6 to the latest versions probably wants to prevent unnecessary class loading even if the JVM does eagerly load referenced classes.That means that this is a very special technique for a very special library and very special circumstances. If you were to include similar techniques in your code I would object such a change for most places, questioning whether this change is really necessary and supported by rigorous performance tests.