简体   繁体   中英

Reflection to avoid class load

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" and err is not null , ie when the class io.perfmark.impl.SecretPerfMarkImpl$PerfMarkImpl is not available or that class has not the required constructor.)

If the code is

Logger.getLogger(PerfMark.class.getName()).log(Level.FINE, "Error during PerfMark.<clinit>", err);

then the java.util.logging.Logger class may be loaded as soon as the PerfMark class is verified and linked (since linking PerfMark requires that the static initializer block is executed).

With this convoluted code the java.util.logging.Logger is only loaded if PerfMark cannot load its support class io.perfmark.impl.SecretPerfMarkImpl$PerfMarkImpl and the system property "io.perfmark.PerfMark.debug" is set to "true" (which probably means that java.util.logging.Logger is almost never loaded just because you use PerfMark )


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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM