“找不到jar文件系统提供程序”,尝试使用jdeps和jlink创建收缩Java映像以运行Minecraft

rekjcdws  于 2023-09-29  发布在  Java
关注(0)|答案(1)|浏览(82)

我正在尝试创建一个收缩Java映像,用于使用jdepsjlink(即只有运行Minecraft所需的最低Java模块),但是我遇到了错误,我希望得到一些指导。我是这样做的:
我将1.20.1 manifest中列出的库下载到我的~/.minecraft/库中,并将client.jar下载到~/. minecraft/versions/1.20.1/1.20.1.jar中。
为了找到Minecraft客户端使用的Java模块,我运行:

jdeps \
    -q \
    --ignore-missing-deps \
    --print-module-deps \
    --multi-release 17 \
    -cp $HOME'/.minecraft/libraries/*' \
    --recursive \
    $HOME/.minecraft/versions/1.20.1/1.20.1.jar

这给了我:

java.base,java.compiler,java.desktop,java.management,java.naming,java.rmi,java.scripting,java.security.jgss,java.sql,jdk.jfr,jdk.unsupported

然后我将这些模块作为$modules传递给jlink

jlink --no-header-files --compress=2 --no-man-pages --add-modules $modules --output 'custom-jre'

最后,我像往常一样运行Minecraft客户端,但现在使用我新创建的收缩JVM

/path/to/custom-jre/bin/java \
    -cp '<whole-list-of-minecraft-jars>' \
    net.minecraft.client.main.Main \
    --version 1.20.1 \
    --gameDir $HOME/.minecraft \
    --accessToken <auth-token>

崩溃并显示错误消息:

Exception in thread "main" java.lang.ExceptionInInitializerError
        at net.minecraft.client.main.Main.main(SourceFile:83)
Caused by: java.lang.IllegalStateException: No jar file system provider found
        at ac.o(SourceFile:101)
        at java.base/java.util.Optional.orElseThrow(Optional.java:403)
        at ac.<clinit>(SourceFile:101)
        ... 1 more

如您所见,客户端成功启动,但在Minecraft源代码中的某个位置存在未处理的异常。
使用MCP(Minecraft源代码的反编译版本),我设法找到了抛出错误的相关代码:

// src/main/java/net/minecraft/Util.java

public class Util {
   // ....

   public static final FileSystemProvider ZIP_FILE_SYSTEM_PROVIDER = FileSystemProvider.installedProviders().stream().filter((p_201865_) -> {
      return p_201865_.getScheme().equalsIgnoreCase("jar");
   }).findFirst().orElseThrow(() -> {
      return new IllegalStateException("No jar file system provider found");
   });

   // ....
}

我对Java文件系统不是很熟悉,但它基本上找不到“jar”文件系统提供程序。我写了这段简短的代码来说明这个问题:

import java.nio.file.spi.FileSystemProvider;

public class Test {
    public static void main(String[] args) {
        for (var provider : FileSystemProvider.installedProviders()) {
            System.out.println(provider.getScheme().toString());
        }
    }
}
# Running with the system's JDK 17.0.8 (~259M)
$ java Test
file
jar
jrt

# Running it with my custom JRE: (~58M)
$ /path/to/custom-jre/bin/java Test
file
jrt # <- doesn't have the jar file system

我在这里描述的方法在我的每个项目中都有效,只有Minecraft导致了这个问题。如果有更好的方法,我很乐意看到它!
那么,什么是适当的解决方案呢?我是不是漏了什么?这个“jar文件系统”是在哪里定义的?任何帮助都是受欢迎的,谢谢提前:)

rqqzpn5f

rqqzpn5f1#

将模块jdk.zipfs添加到您的列表中,该模块应提供zip Filesystem。
另请参阅jdk.zipfs文档,并检查jdeps是否可以帮助您缩小更多的依赖关系。

更新

您似乎已经正确地使用了jdeps来分析模块依赖关系,但不幸的是,它无法帮助动态代码依赖关系,例如基于反射,服务或其他类加载。
您可以使用jdeps而不使用摘要依赖项--print-module-deps来打印更详细的列表,这样可能给予依赖模块中的包中的服务提供者-例如java.nio.file.spi如下所示:

jdeps .....  your.jar
your.jar -> java.base
   ...
   some.module.name     -> java.nio.file.spi      java.base

您可以通过搜索上面链接的JDK文档中的包名称来找到提供程序名称。然后,您可以使用jlink的选项来建议缺少的服务提供程序实现,例如java.nio.file.spi.FileSystemProvider

jlink --suggest-providers java.nio.file.spi.FileSystemProvider

Suggested providers:
  java.base provides java.nio.file.spi.FileSystemProvider used by java.base
  jdk.zipfs provides java.nio.file.spi.FileSystemProvider used by java.base

另一个经常导致jlink打包问题的缺少提供程序是代码需要额外的字符集支持的地方:

jlink --suggest-providers java.nio.charset.spi.CharsetProvider
Suggested providers:
   jdk.charsets provides java.nio.charset.spi.CharsetProvider used by java.base

相关问题