在 Java 17 上使用 Spark 3.3.0 运行单元测试失败,并出现 IllegalAccessError: class StorageUtils 无法访问 class sun.nio.ch.DirectBuffer

2023-11-25

根据发行说明,特别是门票在 Java 17 上构建并运行 Spark (SPARK-33772),Spark现在支持在Java 17上运行。

但是,将 Java 17 (Temurin-17.0.3+7) 与 Maven (3.8.6) 和 maven-surefire-plugin (3.0.0-M7) 一起使用,在运行使用 Spark (3.3.0) 的单元测试时会失败和:

java.lang.IllegalAccessError: class org.apache.spark.storage.StorageUtils$ (in unnamed module @0x1e7ba8d9) cannot access class sun.nio.ch.DirectBuffer (in module java.base) because module java.base does not export sun.nio.ch to unnamed module @0x1e7ba8d9

堆栈是:

java.lang.IllegalAccessError: class org.apache.spark.storage.StorageUtils$ (in unnamed module @0x1e7ba8d9) cannot access class sun.nio.ch.DirectBuffer (in module java.base) because module java.base does not export sun.nio.ch to unnamed module @0x1e7ba8d9
  at org.apache.spark.storage.StorageUtils$.<init>(StorageUtils.scala:213)
  at org.apache.spark.storage.StorageUtils$.<clinit>(StorageUtils.scala)
  at org.apache.spark.storage.BlockManagerMasterEndpoint.<init>(BlockManagerMasterEndpoint.scala:114)
  at org.apache.spark.SparkEnv$.$anonfun$create$9(SparkEnv.scala:353)
  at org.apache.spark.SparkEnv$.registerOrLookupEndpoint$1(SparkEnv.scala:290)
  at org.apache.spark.SparkEnv$.create(SparkEnv.scala:339)
  at org.apache.spark.SparkEnv$.createDriverEnv(SparkEnv.scala:194)
  at org.apache.spark.SparkContext.createSparkEnv(SparkContext.scala:279)
  at org.apache.spark.SparkContext.<init>(SparkContext.scala:464)
  at org.apache.spark.SparkContext$.getOrCreate(SparkContext.scala:2704)
  at org.apache.spark.sql.SparkSession$Builder.$anonfun$getOrCreate$2(SparkSession.scala:953)
  at scala.Option.getOrElse(Option.scala:189)
  at org.apache.spark.sql.SparkSession$Builder.getOrCreate(SparkSession.scala:947)
  [...]

问题Spark 的 Java 17 解决方案 - java.lang.NoClassDefFoundError:无法初始化类 org.apache.spark.storage.StorageUtils仅在 2 个月前被问及,但这早于 Spark 3.3.0 的发布,因此早于对 Java 17 的官方支持。

为什么我无法使用 Java 17 运行 Spark 3.3.0 测试,我们该如何修复它?


尽管Spark现在支持Java 17,但它仍然引用JDK内部类sun.nio.ch.DirectBuffer:

  // In Java 8, the type of DirectBuffer.cleaner() was sun.misc.Cleaner, and it was possible
  // to access the method sun.misc.Cleaner.clean() to invoke it. The type changed to
  // jdk.internal.ref.Cleaner in later JDKs, and the .clean() method is not accessible even with
  // reflection. However sun.misc.Unsafe added a invokeCleaner() method in JDK 9+ and this is
  // still accessible with reflection.
  private val bufferCleaner: DirectBuffer => Unit = [...]

在Java模块系统下,对该类的访问是受到限制的。这Java 9 迁移指南 says:

如果您必须使用默认情况下无法访问的内部 API,则可以使用 --add-exports 命令行选项来打破封装。

我们需要开放对我们的模块的访问。为了对 Surefire 执行此操作,我们将此配置添加到插件中:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>3.0.0-M7</version>
  <configuration>
    <argLine>--add-exports java.base/sun.nio.ch=ALL-UNNAMED</argLine>
  </configuration>
</plugin>

基于一个与一位 Spark 开发人员讨论,Spark 添加以下内容以执行其所有内部单元测试。

这些选项用于传递所有 Spark UT,但也许您不需要全部。

--add-opens=java.base/java.lang=ALL-UNNAMED
--add-opens=java.base/java.lang.invoke=ALL-UNNAMED
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED
--add-opens=java.base/java.io=ALL-UNNAMED
--add-opens=java.base/java.net=ALL-UNNAMED
--add-opens=java.base/java.nio=ALL-UNNAMED
--add-opens=java.base/java.util=ALL-UNNAMED
--add-opens=java.base/java.util.concurrent=ALL-UNNAMED
--add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED
--add-opens=java.base/sun.nio.ch=ALL-UNNAMED
--add-opens=java.base/sun.nio.cs=ALL-UNNAMED
--add-opens=java.base/sun.security.action=ALL-UNNAMED
--add-opens=java.base/sun.util.calendar=ALL-UNNAMED

也有人评论说:

但是,在使用spark-shell、spark-sql和spark-submit时,这些选项不需要显式添加

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Java 17 上使用 Spark 3.3.0 运行单元测试失败,并出现 IllegalAccessError: class StorageUtils 无法访问 class sun.nio.ch.DirectBuffer 的相关文章

随机推荐