反思以避免课堂负担

问题描述 投票:0回答:2

我正在阅读 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 将导致加载记录器类。据我了解,只有在封闭条件为真时才会加载该类。或者这是我想念的点?

提交更多上下文在这里: https://github.com/perfmark/perfmark/commit/4f87fb72c2077df6ade958b524d6d217766c9f93#diff-f9fdc8ad347ee9aa7a11a5259d5ab41c81e84c0ff375de17faebe7625cf50fb5R116


我使用 if 块运行该部分,并在 Logger 类的静态和非静态字段上设置断点。无论使用反射还是直接,它仅在执行调用时才命中断点。当 if 条件为假时,无论如何都不会加载记录器。

java reflection classloader dynamic-class-creation
2个回答
3
投票
我认为该提交的重点是仅在真正需要时才从

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


JVM 规范中有这样的条款,即加载/验证/链接类不需要加载所有引用的类,现代 JVM 实现可能会实现其中的许多要点,以减少不必要的类加载并提高性能。但请记住,

PerfMark

 作为一个支持从 1.6 到最新版本的 Java 版本的非常通用的库,可能希望防止不必要的类加载,即使 JVM 确实急切地加载引用的类。

也就是说,对于一个非常特殊的图书馆和非常特殊的情况,这是一种非常特殊的技术。如果你要在你的代码中包含类似的技术,我会反对在大多数地方进行这样的更改,质疑这种更改是否真的有必要并得到严格的性能测试的支持。


0
投票
(我是本次提交的原作者)

库受一些设计约束,而常规代码则不受。这段代码的目的从表面上很难看出,但它实现了一些目标。

  • 避免拉入

    java.logging

    模块。如果应用程序引入 PerfMark,但它是一个模块化程序,它可能希望避免包含 java.logging。在 IDE 或非模块化应用程序中运行此代码会隐藏问题。通常这些与 
    java.se
     模块一起运行,该模块引入 
    java.logging
    。但是,当运行 
    jlink
     以生成缩小尺寸的 Java 图像时,由于该类不在 
    module path. 上,此代码将抛出 NoClassDefError(或类似错误)

    为了避免强制所有应用程序依赖于

    java.logging

     的模块,如果需要调试,代码会选择反射加载记录器。

  • 为了能够采用库,代码需要尽可能轻量级。大多数时候 PerfMark 将被禁用,因此应避免任何与在其他库中包含 PerfMark 相关的成本。我包括

    benchmarks 在类加载期间显示大约 5ms 的加速。

希望这些能解释改变的动机。如果我知道有更多人在阅读代码,我会包含更好的评论/文档。

© www.soinside.com 2019 - 2024. All rights reserved.