在使用foreach时,Classloader为None加载类



我定义自定义JFR事件如下:

@Name("xxxx.jfr.PerfScope")
@Label("PerfScope scope")
@Category(Array("Java Application"))
@Description("Scope with a tracked performance")
@StackTrace(false)
class PerfScopeEvent(
name: String
) extends jdk.jfr.Event
val hasJFR = try {
FlightRecorder.register(classOf[PerfScopeEvent])
true
} catch {
case _: NoClassDefFoundError =>
// this is expected when running on JDK 8
false
}

使用事件时,代码会执行以下操作:

val event = if (hasJFR) {
Some(new PerfScopeEvent(scopeName))
} else {
None
}
event.foreach(_.begin())
// ....
event.foreach { e =>
e.end()
e.commit()
}

该代码的目的是在Java 8上运行时跳过PerfScopeEvent类功能,其中jdk.jfr.Event类不可用。

我已经在调试器中验证了hasJFR构造良好,当在Java 8上运行时,它是错误的,因为在调用FlightRecorder.register(classOf[PerfScopeEvent])时抛出了异常NoClassDefFoundError

问题在于;使用事件部分";我也有例外。执行event.foreach(_.begin())时引发异常。这让我很困惑。

例外情况是:

Exception in thread "main" java.lang.BootstrapMethodError: java.lang.NoClassDefFoundError: jdk/jfr/Event
Caused by: java.lang.NoClassDefFoundError: jdk/jfr/Event
Caused by: java.lang.ClassNotFoundException: jdk.jfr.Event
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 22 more

我可以通过使用if (event.isDefined) event.foreach(_.begin())来防止异常(第二次使用也是如此(。

if (event.isDefined)中的包装有什么区别?为什么直接使用foreach时类加载器加载类jdk/jfr/Event

我能够创建一个仍然显示相同问题的最小代码:

import jdk.jfr._
object OptionalJFR  {
@Name("com.github.ondrejspanel.jfr.PerfScope")
@Label("PerfScope scope")
@Category(Array("Java Application"))
@Description("Scope with a tracked performance")
@StackTrace(false)
class PerfScopeEvent(@Label("name") val name: String) extends Event
def main(args: Array[String]): Unit = {
val s = (s: PerfScopeEvent) => ()
}
}

由此生成的字节码是:

public final class OptionalJFR$ {
public static final OptionalJFR$ MODULE$;
public static {};
Code:
0: new           #2                  // class OptionalJFR$
3: dup
4: invokespecial #22                 // Method "<init>":()V
7: putstatic     #24                 // Field MODULE$:LOptionalJFR$;
10: return
public void main(java.lang.String[]);
Code:
0: invokedynamic #48,  0             // InvokeDynamic #0:apply:()Lscala/Function1;
5: astore_2
6: return
public static final void $anonfun$main$1(OptionalJFR$PerfScopeEvent);
Code:
0: return
private OptionalJFR$();
Code:
0: aload_0
1: invokespecial #56                 // Method java/lang/Object."<init>":()V
4: return
public static final java.lang.Object $anonfun$main$1$adapted(OptionalJFR$PerfScopeEvent);
Code:
0: aload_0
1: invokestatic  #58                 // Method $anonfun$main$1:(LOptionalJFR$PerfScopeEvent;)V
4: getstatic     #64                 // Field scala/runtime/BoxedUnit.UNIT:Lscala/runtime/BoxedUnit;
7: areturn
private static java.lang.Object $deserializeLambda$(java.lang.invoke.SerializedLambda);
Code:
0: aload_0
1: invokedynamic #76,  0             // InvokeDynamic #1:lambdaDeserialize:(Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object;
6: areturn
}

使用foreach,构造lambda(在本例中为Function[PerfScopeEvent, Unit]的实例(,然后调用foreach。在None的情况下,foreach不执行lambda。

这意味着,即使lambda从未被执行,构造lambda的副作用也会发生。类加载就是这样的副作用。

通过将对foreach的调用封装在if中,直到条件检查之后才创建lambda。

也就是说,一旦你进行了isDefined检查,可能值得考虑打开Some,而不使用map/flatMap/foreach和朋友(不构建lambda会更具性能,尽管有充分的理由保持绝对的风格(。还要注意,eq None几乎肯定比isDefined快。

相关内容

最新更新