如何调试构建本地映像时出现’在图像堆中不允许存在…的实例’的问题?

huangapple go评论79阅读模式
英文:

How do you debug a 'No instances of ... are allowed in the image heap' when building a native image?

问题

以下是您提供的文本的翻译部分:

我有一个小的Java应用程序,使用Micronaut 2.0.0实现了一个RESTful API。在底层,它使用Redisson 3.13.1来访问Redis。而Redisson又使用了Netty(4.1.49)。

这个应用程序在传统的Java环境(在HotSpot上,包括Java 8和11)中可以正常运行。

我正在尝试使用GraalVM构建这个应用程序的本地镜像。

命令大致如下:

native-image --no-server --no-fallback -H:+TraceClassInitialization -H:+PrintClassInitialization --report-unsupported-elements-at-runtime --initialize-at-build-time=reactor.core.publisher.Flux,reactor.core.publisher.Mono -H:ConfigurationFileDirectories=target/config -cp target/app-1.0.0-SNAPSHOT.jar com.app.AppApplication target/app

以下是我得到的错误信息:

错误:4个方法中不支持的功能
详细消息:
错误:不允许在图像堆中存在java.net.Inet4Address的实例,因为此类应在图像运行时初始化。该对象已在没有本地映像初始化仪器的情况下初始化,无法跟踪堆栈跟踪。
跟踪:对象已被
读取常量io.netty.channel.socket.InternetProtocolFamily.localHost
        常量io.netty.channel.socket.InternetProtocolFamily@593f1f62
        扫描方法io.netty.resolver.dns.DnsNameResolver.preferredAddressType(DnsNameResolver.java:481)
从入口点到io.netty.resolver.dns.DnsNameResolver.preferredAddressType(ResolvedAddressTypes)的呼叫路径:
        在io.netty.resolver.dns.DnsNameResolver.preferredAddressType(DnsNameResolver.java:478)
        在io.netty.resolver.dns.DnsNameResolver.<init>(DnsNameResolver.java:436)
        在io.netty.resolver.dns.DnsNameResolverBuilder.build(DnsNameResolverBuilder.java:473)
        在io.netty.resolver.dns.DnsAddressResolverGroup.newNameResolver(DnsAddressResolverGroup.java:111)
        在io.netty.resolver.dns.DnsAddressResolverGroup.newResolver(DnsAddressResolverGroup.java:91)
        在io.netty.resolver.dns.DnsAddressResolverGroup.newResolver(DnsAddressResolverGroup.java:76)
        在io.netty.resolver.AddressResolverGroup.getResolver(AddressResolverGroup.java:70)
        在org.redisson.cluster.ClusterConnectionManager$1.run(ClusterConnectionManager.java:251)
        在com.oracle.svm.core.jdk.RuntimeSupport.executeHooks(RuntimeSupport.java:125)
        在com.oracle.svm.core.jdk.RuntimeSupport.executeStartupHooks(RuntimeSupport.java:75)
        在com.oracle.svm.core.JavaMainWrapper.runCore(JavaMainWrapper.java:141)
        在com.oracle.svm.core.JavaMainWrapper.run(JavaMainWrapper.java:184)
        在com.oracle.svm.core.code.IsolateEnterStub.JavaMainWrapper_run_5087f5482cc9a6abc971913ece43acb471d2631b(generated:0)

这只是输出的一部分,它还会在其他3个错误上产生类似的报告。

我仍然在努力理解问题,但我认为,由于java.net.InetAddress中有本地方法,因此无论是它本身还是它的子类java.net.Inet4Address都不能在构建时初始化。这意味着Inet4Address的实例不能在构建时初始化的代码中可见(从Java术语来说,是在初始化阶段)。而本地镜像构建器找到了一种方式,可以在某种程度上达到一个这样的对象可见的点。它甚至显示了跟踪,但问题是ClusterConnectionManager$1只是在运行时(在静态初始化之后很久)提交给Executor的一个Runnable

您如何调试这种情况呢?具体来说:

  1. 如何找到问题的根本原因?
  2. 当找到问题的原因后,如何修复它?

PS. 如果我添加了--initialize-at-run-time=java.net.InetAddress,它会以不同的方式失败:

错误:java.net.InetAddress类已经被初始化;现在注册java.net.InetAddress以进行构建时初始化(从命令行)。java.net.InetAddress已在没有本地映像初始化仪器的情况下初始化,无法跟踪堆栈跟踪。尝试通过避免初始化导致java.net.InetAddress初始化的类来避免此冲突,或者不标记java.net.InetAddress以进行构建时初始化。

Java自己报告为build 25.252-b09-jvmci-20.1-b02,混合模式

PPS. 我发现了这个https://stackoverflow.com/questions/59011565/no-instances-of-are-allowed-in-the-image-heap-as-this-class-should-be-initia的问题,似乎Quarkus的问题已经解决了。但我仍然不明白如何解决当前的问题。任何帮助将不胜感激。

英文:

I have a little Java application that implements a RESTful API using Micronaut 2.0.0. Under the hood, it uses Redisson 3.13.1 to go to Redis. Redisson, in turn, uses Netty (4.1.49).

The application works fine in a 'classic' java (on HotSpot, both Java 8 and 11).

I'm trying to build a native image out of this application using GraalVM.

The command is approximately like this:

native-image --no-server --no-fallback -H:+TraceClassInitialization -H:+PrintClassInitialization --report-unsupported-elements-at-runtime --initialize-at-build-time=reactor.core.publisher.Flux,reactor.core.publisher.Mono -H:ConfigurationFileDirectories=target/config -cp target/app-1.0.0-SNAPSHOT.jar com.app.AppApplication target/app

Here is what I get:

Error: Unsupported features in 4 methods
Detailed message:
Error: No instances of java.net.Inet4Address are allowed in the image heap as this class should be initialized at image runtime. Object has been initialized without the native-image initialization instrumentation and the stack trace can&#39;t be tracked.
Trace: Object was reached by 
    reading field io.netty.channel.socket.InternetProtocolFamily.localHost of
        constant io.netty.channel.socket.InternetProtocolFamily@593f1f62 reached by 
    scanning method io.netty.resolver.dns.DnsNameResolver.preferredAddressType(DnsNameResolver.java:481)
Call path from entry point to io.netty.resolver.dns.DnsNameResolver.preferredAddressType(ResolvedAddressTypes): 
    at io.netty.resolver.dns.DnsNameResolver.preferredAddressType(DnsNameResolver.java:478)
    at io.netty.resolver.dns.DnsNameResolver.&lt;init&gt;(DnsNameResolver.java:436)
    at io.netty.resolver.dns.DnsNameResolverBuilder.build(DnsNameResolverBuilder.java:473)
    at io.netty.resolver.dns.DnsAddressResolverGroup.newNameResolver(DnsAddressResolverGroup.java:111)
    at io.netty.resolver.dns.DnsAddressResolverGroup.newResolver(DnsAddressResolverGroup.java:91)
    at io.netty.resolver.dns.DnsAddressResolverGroup.newResolver(DnsAddressResolverGroup.java:76)
    at io.netty.resolver.AddressResolverGroup.getResolver(AddressResolverGroup.java:70)
    at org.redisson.cluster.ClusterConnectionManager$1.run(ClusterConnectionManager.java:251)
    at com.oracle.svm.core.jdk.RuntimeSupport.executeHooks(RuntimeSupport.java:125)
    at com.oracle.svm.core.jdk.RuntimeSupport.executeStartupHooks(RuntimeSupport.java:75)
    at com.oracle.svm.core.JavaMainWrapper.runCore(JavaMainWrapper.java:141)
    at com.oracle.svm.core.JavaMainWrapper.run(JavaMainWrapper.java:184)
    at com.oracle.svm.core.code.IsolateEnterStub.JavaMainWrapper_run_5087f5482cc9a6abc971913ece43acb471d2631b(generated:0)

That's just a part of the output, it also produces similar reports on other 3 errors.

I'm still struggling to understand the issue, but I suppose that, as java.net.InetAddress has native methods in it, neither it nor its subclass java.net.Inet4Address can be initialized at build time. This means that an instance of Inet4Address cannot be visible for a code that is initialized at build time (at initialization stage, in Java terms). And the native image builder found a way that kinda reaches a point where such an object is visible. It even shows the trace, but the thing is that ClusterConnectionManager$1 is a Runnable that is only submitted to an Executor at runtime (waaaay after the static initialization).

How do you debug such situations? Namely:

  1. How do you find the culprit?
  2. How do you fix it when the culprit is found?

PS. If I add --initialize-at-run-time=java.net.InetAddress, it fails differently:

Error: The class java.net.InetAddress has already been initialized; it is too late 
to register java.net.InetAddress for build-time initialization (from the command 
line). java.net.InetAddress has been initialized without the native-image 
initialization instrumentation and the stack trace can&#39;t be tracked. Try avoiding 
this conflict by avoiding to initialize the class that caused initialization of 
java.net.InetAddress or by not marking java.net.InetAddress for build-time 
initialization.

Java reports itself as build 25.252-b09-jvmci-20.1-b02, mixed mode.

PPS. I found this https://stackoverflow.com/questions/59011565/no-instances-of-are-allowed-in-the-image-heap-as-this-class-should-be-initia and it seems that the Quarkus issue was fixed. But I still do not understand how to fix the issue at hand. Any help would be appreciated.

答案1

得分: 24

# 模块1: 主题和内容生成
class TopicGenerator:
    def generate_topics(self, user_input):
        # 根据用户输入生成研究主题候选列表
        topics = []  # 生成的主题列表
        # 实现逻辑
        return topics

class KeywordGenerator:
    def generate_keywords(self, selected_topic):
        # 根据选定的研究主题生成关键词
        keywords = []  # 生成的关键词列表
        # 实现逻辑
        return keywords

class ContentGenerator:
    def generate_content(self, outline, keywords):
        # 根据大纲和关键词生成论文内容
        content = ""  # 生成的论文内容
        # 实现逻辑
        return content

class AbstractConclusionGenerator:
    def generate_abstract(self, full_content):
        # 根据全文内容生成摘要
        abstract = ""  # 生成的摘要
        # 实现逻辑
        return abstract

    def generate_conclusion(self, full_content):
        # 根据全文内容生成结论
        conclusion = ""  # 生成的结论
        # 实现逻辑
        return conclusion

# 模块2: 文献处理
class LiteratureDatabaseSearch:
    def search_literature(self, keywords):
        # 使用关键词在学术数据库中搜索相关文献
        literature_list = []  # 相关文献列表
        # 实现逻辑
        return literature_list

class LiteratureFilterAndCategorize:
    def filter_and_categorize_literature(self, literature_list, selected_topic):
        # 自动筛选出高质量和相关的文献,并按照主题或方法进行分类
        filtered_literature = []  # 筛选后的文献列表
        # 实现逻辑
        return filtered_literature

class CitationReferenceGenerator:
    def generate_citations(self, literature_list):
        # 自动格式化引用和生成参考文献列表
        citations = ""  # 生成的引用和参考文献
        # 实现逻辑
        return citations

# 模块3: 内容优化
class ContentOptimizer:
    def optimize_content(self, generated_content):
        # 对自动生成的内容进行语法、用词和逻辑优化
        optimized_content = ""  # 优化后的内容
        # 实现逻辑
        return optimized_content

class ParagraphPolisher:
    def polish_paragraphs(self, content):
        # 确保论文的逻辑性和连贯性,对段落进行调整和优化
        polished_content = ""  # 优化后的内容
        # 实现逻辑
        return polished_content

class SelfValidation:
    def validate_content(self, content):
        # 使用内置工具对内容进行准确性和原创性检查
        validation_result = False  # 验证结果
        # 实现逻辑
        return validation_result

# 模块4: 格式和排版
class FormattingModule:
    def format_paper(self, content, journal_format):
        # 根据指定的期刊格式要求,自动调整论文的格式
        formatted_paper = ""  # 格式化后的论文
        # 实现逻辑
        return formatted_paper

class ExportModule:
    def export_paper(self, formatted_paper, export_format):
        # 将完成的论文导出为指定的文件格式,如PDF
        exported_paper = ""  # 导出的论文文件
        # 实现逻辑
        return exported_paper

# 模块5: 图表和数据处理
class ChartDataProcessor:
    def insert_charts_data(self, content):
        # 根据内容需要,自动选择和插入相关的图表和数据
        content_with_charts = ""  # 包含图表和数据的内容
        # 实现逻辑
        return content_with_charts

class ChartGenerator:
    def generate_chart(self, chart_data):
        # 生成具备清晰标签和标题的图表
        chart = ""  # 生成的图表
        # 实现逻辑
        return chart

# 模块6: 语言处理
class LanguageProcessor:
    def process_language(self, content):
        # 确保准确、规范的中文表达和学术风格
        processed_content = ""  # 处理后的内容
        # 实现逻辑
        return processed_content

# 模块7: 用户交互
class UserInterfaceModule:
    def provide_user_interface(self):
        # 提供用户交互界面
        # 实现逻辑
        pass

# 模块8: 程序运行和异常处理
class ProgramRunner:
    def run_program(self):
        # 确保程序各模块正常运行
        # 实现逻辑
        pass

# 模块9: 其他模块
class PaperSearchModule:
    def search_related_papers(self, selected_topic):
        # 获取与主题相关的论文列表
        related_papers = []  # 相关论文列表
        # 实现逻辑
        return related_papers

class OutlineGenerator:
    def generate_paper_outline(self):
        # 生成论文题纲
        paper_outline = ""  # 生成的论文题纲
        # 实现逻辑
        return paper_outline

class PaperContentWriter:
    def write_paper_content(self, outline, related_literature):
        # 自动生成论文内容
        paper_content = ""  # 自动生成的论文内容
        # 实现逻辑
        return paper_content

# 创建模块实例
topic_generator = TopicGenerator()
keyword_generator = KeywordGenerator()
content_generator = ContentGenerator()
abstract_conclusion_generator = AbstractConclusionGenerator()
literature_db_search = LiteratureDatabaseSearch()
literature_filter_categorize = LiteratureFilterAndCategorize()
citation_reference_generator = CitationReferenceGenerator()
content_optimizer = ContentOptimizer()
paragraph_polisher = ParagraphPolisher()
self_validation = SelfValidation()
formatting_module = FormattingModule()
export_module = ExportModule()
chart_data_processor = ChartDataProcessor()
chart_generator = ChartGenerator()
language_processor = LanguageProcessor()
user_interface_module = UserInterfaceModule()
program_runner = ProgramRunner()
paper_search_module = PaperSearchModule()
outline_generator = OutlineGenerator()
paper_content_writer = PaperContentWriter()
英文:

TLDR; there is a small section with a summary at the end of the answer.

A bit of theory

In Java, every class must be initialized before usage. Initialization means executing static field initializers
and static initialization blocks. In a standard JVM (like HotSpot), this happens at runtime, of course.

But with a native image, you have two alternatives. A class may still be initialized at runtime, or its initialization
may be carried out at build time. The latter has an obvious benefit of avoiding this work at native image startup,
which makes the image start faster. But for some classes it does not make sense to initialize them at build time.
Such an example could be a class that makes some decisions in its initialization (create an instance of this or that
class, for example) based on environment (env variables, config files, etc).

There are some restrictions in regard to choosing between build/run time initialization alternatives:

  • If a class is initialized at build time, all of its superclasses must be initialized at build time
  • If a class is initialized at run time, all of its subclasses must be initialized at runtime
  • Some classes must always be initialized at runtime (see below)
  • An instance of a class that is initialized at runtime cannot exist in image heap (this means that no
    build-time initialized class or its instance can (directly or indirectly) reference such a runtime-initialized
    class instance
    )

InetAddress issue

Since version 19.3.0, native-image tool requires that java.net.InetAddress class is always initialized at runtime.
This (by restriction 2) means that its subclasses, java.net.Inet4Address and java.net.Inet6Address must also be
initialized at runtime, which in turn (by restriction 4) means that you cannot have any InetAddress referenced
by a build-time initialized class.

All the build failures we encounter here are caused by this same problem: either Inet4Address or Inet6Address
in image heap.

But why are Netty-related classes tried to be initialized at build time?

It turns out that netty-codec-http contains the following

Args = --initialize-at-build-time=io.netty \

in its native-image.properties below META-INF, and micronaut has netty-codec-http as a dependency, so all
io.netty classes are by default initialized at build time (as native-image tool respects such
native-image.properties files).

A model project

Here https://github.com/rpuch/netty-InetAddress-native-image-diagnosing is a project that models the problem and which
I further use to show how to solve the problem. Its main() method follows:

public static void main(String[] args) throws Exception {
NioEventLoopGroup group = new NioEventLoopGroup(1, new DefaultThreadFactory(&quot;netty&quot;));
DnsAddressResolverGroup resolverGroup = new DnsAddressResolverGroup(NioDatagramChannel.class,
DnsServerAddressStreamProviders.platformDefault());
AddressResolver&lt;InetSocketAddress&gt; resolver = resolverGroup.getResolver(group.next());
System.out.println(resolver);
resolver.close();
group.shutdownGracefully().get();
}

It causes the same effects (regarding Netty) that the following code does:

    Config config = new Config();
config.useSingleServer().setAddress(redisUri);
config.useSingleServer().setPassword(redisPassword);
return Redisson.createReactive(config);

This project also has --initialize-at-build-time=io.netty in its build script to emulate micronaut-based project
behavior.

So it is a useful substitute for the original project that brought this question to light.

GraalVM version

I'm using version 20.2.0 here (the most recent released version as of the moment of writing).

Diagnosing and fixing

1

Build fails with the following error:

Error: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException: No instances of java.net.Inet4Address are allowed in the image heap as this class should be initialized at image runtime. Object has been initialized without the native-image initialization instrumentation and the stack trace can&#39;t be tracked.
Trace:
at parsing io.netty.resolver.dns.DnsNameResolver.resolveHostsFileEntry(DnsNameResolver.java:659)
Call path from entry point to io.netty.resolver.dns.DnsNameResolver.resolveHostsFileEntry(String):
at io.netty.resolver.dns.DnsNameResolver.resolveHostsFileEntry(DnsNameResolver.java:651)
at io.netty.resolver.dns.DnsNameResolver.doResolve(DnsNameResolver.java:884)
at io.netty.resolver.dns.DnsNameResolver.doResolve(DnsNameResolver.java:733)
at io.netty.resolver.SimpleNameResolver.resolve(SimpleNameResolver.java:61)
at io.netty.resolver.SimpleNameResolver.resolve(SimpleNameResolver.java:53)
at io.netty.resolver.InetSocketAddressResolver.doResolve(InetSocketAddressResolver.java:55)
at io.netty.resolver.InetSocketAddressResolver.doResolve(InetSocketAddressResolver.java:31)
at io.netty.resolver.AbstractAddressResolver.resolve(AbstractAddressResolver.java:106)
at io.netty.bootstrap.Bootstrap.doResolveAndConnect0(Bootstrap.java:206)
at io.netty.bootstrap.Bootstrap.access$000(Bootstrap.java:46)
at io.netty.bootstrap.Bootstrap$1.operationComplete(Bootstrap.java:180)

DnsNameResolver:659 is

return LOCALHOST_ADDRESS;

and it references the static field named LOCALHOST_ADDRESS of type InetAddress. Let's avoid its initialization
at build time by adding the following to the native-image command`:

--initialize-at-run-time=io.netty.resolver.dns.DnsNameResolver

The error goes away.

2

Now there is another one:

Error: No instances of java.net.Inet6Address are allowed in the image heap as this class should be initialized at image runtime. Object has been initialized without the native-image initialization instrumentation and the stack trace can&#39;t be tracked.
Trace: Object was reached by
reading field java.util.HashMap$Node.value of
constant java.util.HashMap$Node@26eb0f30 reached by
indexing into array
constant java.util.HashMap$Node[]@63e95621 reached by
reading field java.util.HashMap.table of
constant java.util.HashMap@563992d1 reached by
reading field java.util.Collections$UnmodifiableMap.m of
constant java.util.Collections$UnmodifiableMap@38a9945c reached by
reading field io.netty.resolver.DefaultHostsFileEntriesResolver.inet6Entries of
constant io.netty.resolver.DefaultHostsFileEntriesResolver@7ef4ba7e reached by
scanning method io.netty.resolver.dns.DnsNameResolverBuilder.&lt;init&gt;(DnsNameResolverBuilder.java:56)
Call path from entry point to io.netty.resolver.dns.DnsNameResolverBuilder.&lt;init&gt;():
at io.netty.resolver.dns.DnsNameResolverBuilder.&lt;init&gt;(DnsNameResolverBuilder.java:68)
at io.netty.resolver.dns.DnsAddressResolverGroup.&lt;init&gt;(DnsAddressResolverGroup.java:54)
at Main.main(Main.java:18)

DnsNameResolverBuilder:56 is

private HostsFileEntriesResolver hostsFileEntriesResolver = HostsFileEntriesResolver.DEFAULT;

Let's postpone HostsFileEntriesResolver initialization:

--initialize-at-run-time=io.netty.resolver.HostsFileEntriesResolver

3

Now, there is another error:

Error: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException: No instances of java.net.Inet6Address are allowed in the image heap as this class should be initialized at image runtime. Object has been initialized without the native-image initialization instrumentation and the stack trace can&#39;t be tracked.
Trace:
at parsing io.netty.resolver.dns.DnsQueryContextManager.getOrCreateContextMap(DnsQueryContextManager.java:111)
Call path from entry point to io.netty.resolver.dns.DnsQueryContextManager.getOrCreateContextMap(InetSocketAddress):
at io.netty.resolver.dns.DnsQueryContextManager.getOrCreateContextMap(DnsQueryContextManager.java:96)

DnsQueryContextManager:111 references NetUtil.LOCALHOST6. NetUtil is initialized at build time, but its fields
LOCALHOST4 and LOCALHOST6 contain instances of Inet4Address and Inet6Address, respectively. This can be solved
by a substitution. We just add the following class to our project:

@TargetClass(NetUtil.class)
final class NetUtilSubstitutions {
@Alias
@InjectAccessors(NetUtilLocalhost4Accessor.class)
public static Inet4Address LOCALHOST4;
@Alias
@InjectAccessors(NetUtilLocalhost6Accessor.class)
public static Inet6Address LOCALHOST6;
private static class NetUtilLocalhost4Accessor {
static Inet4Address get() {
// using https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom
return NetUtilLocalhost4LazyHolder.LOCALHOST4;
}
static void set(Inet4Address ignored) {
// a no-op setter to avoid exceptions when NetUtil is initialized at run-time
}
}
private static class NetUtilLocalhost4LazyHolder {
private static final Inet4Address LOCALHOST4;
static {
byte[] LOCALHOST4_BYTES = {127, 0, 0, 1};
// Create IPv4 loopback address.
try {
LOCALHOST4 = (Inet4Address) InetAddress.getByAddress(&quot;localhost&quot;, LOCALHOST4_BYTES);
} catch (Exception e) {
// We should not get here as long as the length of the address is correct.
PlatformDependent.throwException(e);
throw new IllegalStateException(&quot;Should not reach here&quot;);
}
}
}
private static class NetUtilLocalhost6Accessor {
static Inet6Address get() {
// using https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom
return NetUtilLocalhost6LazyHolder.LOCALHOST6;
}
static void set(Inet6Address ignored) {
// a no-op setter to avoid exceptions when NetUtil is initialized at run-time
}
}
private static class NetUtilLocalhost6LazyHolder {
private static final Inet6Address LOCALHOST6;
static {
byte[] LOCALHOST6_BYTES = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
// Create IPv6 loopback address.
try {
LOCALHOST6 = (Inet6Address) InetAddress.getByAddress(&quot;localhost&quot;, LOCALHOST6_BYTES);
} catch (Exception e) {
// We should not get here as long as the length of the address is correct.
PlatformDependent.throwException(e);
throw new IllegalStateException(&quot;Should not reach here&quot;);
}
}
}
}

The idea is to replace loads of the problematic fields with method invocations controlled by us. This is achieved
via a substitution (note @TargetClass, @Alias and @InjectAccessors). As a result, the InetAddress values
are not stored in image heap anymore. The error goes away.

4

We now have another one:

Error: No instances of java.net.Inet6Address are allowed in the image heap as this class should be initialized at image runtime. Object has been initialized without the native-image initialization instrumentation and the stack trace can&#39;t be tracked.
Trace: Object was reached by
reading field io.netty.channel.socket.InternetProtocolFamily.localHost of
constant io.netty.channel.socket.InternetProtocolFamily@5dc39065 reached by
scanning method io.netty.resolver.dns.DnsNameResolver.preferredAddressType(DnsNameResolver.java:487)
Call path from entry point to io.netty.resolver.dns.DnsNameResolver.preferredAddressType(ResolvedAddressTypes):
at io.netty.resolver.dns.DnsNameResolver.preferredAddressType(DnsNameResolver.java:481)

As it can be seen from the code of InternetProtocolFamily, each enum constant stores an instance of InetAddress,
so if any class initialized at build time initializes InternetProtocolFamily, the image heap gets polluted with
InetAddress instance. This can also be solved with a substitution:

@TargetClass(InternetProtocolFamily.class)
final class InternetProtocolFamilySubstitutions {
@Alias
@InjectAccessors(InternetProtocolFamilyLocalhostAccessor.class)
private InetAddress localHost;
private static class InternetProtocolFamilyLocalhostAccessor {
static InetAddress get(InternetProtocolFamily family) {
switch (family) {
case IPv4:
return NetUtil.LOCALHOST4;
case IPv6:
return NetUtil.LOCALHOST6;
default:
throw new IllegalStateException(&quot;Unsupported internet protocol family: &quot; + family);
}
}
static void set(InternetProtocolFamily family, InetAddress address) {
// storing nothing as the getter derives all it needs from its argument
}
}
}

The error goes away.

5

There is another one this time:

Error: No instances of java.net.Inet4Address are allowed in the image heap as this class should be initialized at image runtime. Object has been initialized without the native-image initialization instrumentation and the stack trace can&#39;t be tracked.
Detailed message:
Trace: Object was reached by
reading field java.net.InetSocketAddress$InetSocketAddressHolder.addr of
constant java.net.InetSocketAddress$InetSocketAddressHolder@34913c36 reached by
reading field java.net.InetSocketAddress.holder of
constant java.net.InetSocketAddress@ad1fe10 reached by
reading field io.netty.resolver.dns.SingletonDnsServerAddresses.address of
constant io.netty.resolver.dns.SingletonDnsServerAddresses@79fd599 reached by
scanning method io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.nameServerAddressStream(DefaultDnsServerAddressStreamProvider.java:115)
Call path from entry point to io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.nameServerAddressStream(String):
at io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.nameServerAddressStream(DefaultDnsServerAddressStreamProvider.java:115)
at io.netty.resolver.dns.DnsServerAddressStreamProviders$DefaultProviderHolder$1.nameServerAddressStream(DnsServerAddressStreamProviders.java:131)
at io.netty.resolver.dns.DnsNameResolver.doResolveAllUncached0(DnsNameResolver.java:1070)

First, let's move initialization of io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider to
run-time:

--initialize-at-run-time=io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider

Now, the error is similar, but still slightly different:

Error: No instances of java.net.Inet4Address are allowed in the image heap as this class should be initialized at image runtime. Object has been initialized without the native-image initialization instrumentation and the stack trace can&#39;t be tracked.
Trace: Object was reached by
reading field java.net.InetSocketAddress$InetSocketAddressHolder.addr of
constant java.net.InetSocketAddress$InetSocketAddressHolder@5537c5de reached by
reading field java.net.InetSocketAddress.holder of
constant java.net.InetSocketAddress@fb954f8 reached by
reading field io.netty.resolver.dns.SingletonDnsServerAddresses.address of
constant io.netty.resolver.dns.SingletonDnsServerAddresses@3ec9baab reached by
reading field io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider.defaultNameServerAddresses of
constant io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider@1b7f0339 reached by
reading field io.netty.resolver.dns.DnsServerAddressStreamProviders$DefaultProviderHolder$1.currentProvider of
constant io.netty.resolver.dns.DnsServerAddressStreamProviders$DefaultProviderHolder$1@2d249be7 reached by
scanning method io.netty.resolver.dns.DnsServerAddressStreamProviders.unixDefault(DnsServerAddressStreamProviders.java:104)
Call path from entry point to io.netty.resolver.dns.DnsServerAddressStreamProviders.unixDefault():
at io.netty.resolver.dns.DnsServerAddressStreamProviders.unixDefault(DnsServerAddressStreamProviders.java:104)
at io.netty.resolver.dns.DnsServerAddressStreamProviders.platformDefault(DnsServerAddressStreamProviders.java:100)
at Main.main(Main.java:18)

Ok, let's move initialization of io.netty.resolver.dns.DnsServerAddressStreamProviders$DefaultProviderHolder to
run-time as well:

&#39;--initialize-at-run-time=io.netty.resolver.dns.DnsServerAddressStreamProviders$DefaultProviderHolder&#39;

(note the single quotes: without them $ and characters following it will be interpreted by sh and replaced with an
empty string).

The error goes away.

>Please note that the order turned out to be important here. When I first moved
io.netty.resolver.dns.DnsServerAddressStreamProviders$DefaultProviderHolder initialization to run-time but did not
touch io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider initialization, the error report did not change
a bit. So it takes a little patience and experimenting (or some knowledge which I do not have, alas).

Now we have this:

Error: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException: No instances of java.net.Inet6Address are allowed in the image heap as this class should be initialized at image runtime. Object has been initialized without the native-image initialization instrumentation and the stack trace can&#39;t be tracked.
Detailed message:
Trace:
at parsing io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.&lt;clinit&gt;(DefaultDnsServerAddressStreamProvider.java:87)
Call path from entry point to io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.&lt;clinit&gt;():
no path found from entry point to target method

Ok, it's NetUtil.LOCALHOST being referenced, so let's add a substitution for it as well (to NetUtilSubstitutions):

@Alias
@InjectAccessors(NetUtilLocalhostAccessor.class)
public static InetAddress LOCALHOST;
// NOTE: this is the simpliest implementation I could invent to just demonstrate the idea; it is probably not
// too efficient. An efficient implementation would only have getter and it would compute the InetAddress
// there; but the post is already very long, and NetUtil.LOCALHOST computation logic in Netty is rather cumbersome.
private static class NetUtilLocalhostAccessor {
private static volatile InetAddress ADDR;
static InetAddress get() {
return ADDR;
}
static void set(InetAddress addr) {
ADDR = addr;
}
}

This makes the final error go away.

>Thanks to @NicolasFilotto for suggestions on item 5, I like his solution a lot more than the original, and actually
item 5 is an implementation of his ideas.

Summary of techniques

  1. First of all, you could find a class that, being moved to runtime initialization phase, makes a failure to go away.
    To do this, you could trace references in the provided stack trace. Best candidates to consider are static fields.
  2. If item 1 does not help, you could try to create a substitution
  3. There is also a theoretical possibility to go with a lighter variant: @RecomputeFieldValue, but it's more
    restricted, and I could not make it work in this Netty-related task.

PS. Substitution-related code is inspired by https://github.com/quarkusio/quarkus/pull/5353/files

PPS. Item 5 solution is inspired by @NicolasFilotto

huangapple
  • 本文由 发表于 2020年8月9日 23:43:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/63328298.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定