我正在阅读 PerfMark 代码,看到一条关于通过在提交中使用反射来避免意外类加载的评论:
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());
..
}
不太明白这里防止误加载哪个类。根据 Class#forName 将导致加载记录器类。据我了解,只有在封闭条件为真时才会加载该类。或者这是我想念的点?
java.util.logging
加载类(当系统属性“io.perfmark.PerfMark.debug”为“true”并且
err
不是
null
,即当类
io.perfmark.impl.SecretPerfMarkImpl$PerfMarkImpl
不可用或该类没有所需的构造函数时。)如果密码是
Logger.getLogger(PerfMark.class.getName()).log(Level.FINE, "Error during PerfMark.<clinit>", err);
然后一旦java.util.logging.Logger
类被验证和链接就可以加载
PerfMark
类(因为链接
PerfMark
需要执行静态初始化块)。有了这个复杂的代码,
java.util.logging.Logger
只有在
PerfMark
无法加载其支持类
io.perfmark.impl.SecretPerfMarkImpl$PerfMarkImpl
和系统属性“io.perfmark.PerfMark.debug”设置为“true”(这可能意味着java.util.logging.Logger
几乎从不加载只是因为你使用
PerfMark
)
PerfMark
作为一个支持从 1.6 到最新版本的 Java 版本的非常通用的库,可能希望防止不必要的类加载,即使 JVM 确实急切地加载引用的类。也就是说,对于一个非常特殊的图书馆和非常特殊的情况,这是一种非常特殊的技术。如果你要在你的代码中包含类似的技术,我会反对在大多数地方进行这样的更改,质疑这种更改是否真的有必要并得到严格的性能测试的支持。
库受一些设计约束,而常规代码则不受。这段代码的目的从表面上很难看出,但它实现了一些目标。
java.logging
模块。如果应用程序引入 PerfMark,但它是一个模块化程序,它可能希望避免包含 java.logging。在 IDE 或非模块化应用程序中运行此代码会隐藏问题。通常这些与
java.se
模块一起运行,该模块引入
java.logging
。但是,当运行
jlink
以生成缩小尺寸的 Java 图像时,由于该类不在module path. 上,此代码将抛出 NoClassDefError(或类似错误) 为了避免强制所有应用程序依赖于
java.logging
的模块,如果需要调试,代码会选择反射加载记录器。
benchmarks 在类加载期间显示大约 5ms 的加速。