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

Gre*_*pff 14 java apache-spark java-17

根据发行说明,特别是在 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)
  [...]
Run Code Online (Sandbox Code Playgroud)

Spark 的 Java 17 解决方案- java.lang.NoClassDefFoundError: Could not initialize class org.apache.spark.storage.StorageUtils 问题仅在 2 个月前提出,但这早于 Spark 3.3.0 版本,因此早于官方版本支持 Java 17。

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

Gre*_*pff 15

尽管 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 = [...]
Run Code Online (Sandbox Code Playgroud)

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

如果您必须使用默认情况下无法访问的内部 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>
Run Code Online (Sandbox Code Playgroud)

根据与 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
Run Code Online (Sandbox Code Playgroud)

也有人评论说:

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


Ant*_*eev 5

根据上面的讨论,我正在使用:

%SPARK_HOME%\bin\spark-submit.cmd --driver-java-options "--add-exports java.base/sun.nio.ch=ALL-UNNAMED" spark_ml_heart.py
Run Code Online (Sandbox Code Playgroud)

只需--add-exports在 Java 17 上的 Spark 3.2.1 上运行 Python 脚本即可。

您可能需要包含以下所有内容的完整版本--add-exports

%SPARK_HOME%\bin\spark-submit.cmd --driver-java-options "--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_ml_heart.py
Run Code Online (Sandbox Code Playgroud)