当使用 foreach 时,类加载器为 None 加载 class
Classloader loading a class for None when foreach is used on it
我这样定义自定义 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
class 功能,其中 class jdk.jfr.Event
不可用。
我已经在调试器中验证了 hasJFR
构造良好,当 运行 在 Java 8 上时它是错误的,因为调用 NoClassDefFoundError
时抛出异常 FlightRecorder.register(classOf[PerfScopeEvent])
.
问题是在“使用事件部分”中我也遇到了异常。执行 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
时classloader会加载classjdk/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(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$adapted(OptionalJFR$PerfScopeEvent);
Code:
0: aload_0
1: invokestatic #58 // Method $anonfun$main:(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
.
快
我这样定义自定义 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
class 功能,其中 class jdk.jfr.Event
不可用。
我已经在调试器中验证了 hasJFR
构造良好,当 运行 在 Java 8 上时它是错误的,因为调用 NoClassDefFoundError
时抛出异常 FlightRecorder.register(classOf[PerfScopeEvent])
.
问题是在“使用事件部分”中我也遇到了异常。执行 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
时classloader会加载classjdk/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(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$adapted(OptionalJFR$PerfScopeEvent);
Code:
0: aload_0
1: invokestatic #58 // Method $anonfun$main:(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
.