micronaut-projects / micronaut-core

Micronaut Application Framework
http://micronaut.io
Apache License 2.0
6.1k stars 1.07k forks source link

nativeTest fails with minimal/default Kotlin Gradle project created from micronaut launch #10646

Open erkieh opened 8 months ago

erkieh commented 8 months ago

Expected Behavior

Tests succeed

Actual Behaviour

Errors are thrown about objects "found in the image heap. This type, however, is marked for initialization at image run time for the following reason: classes are initialized at run time by default"

I have attached logs with Kotest and Junit.

logs.txt

Steps To Reproduce

curl --location --request GET 'https://launch.micronaut.io/create/default/com.example.demo?lang=KOTLIN&build=GRADLE_KOTLIN&test=KOTEST&javaVersion=JDK_21' --output demo.zip

gradle nativeTest

Environment Information

Windows 10 and GraalVM for JDK 21

Example Application

curl --location --request GET 'https://launch.micronaut.io/create/default/com.example.demo?lang=KOTLIN&build=GRADLE_KOTLIN&test=KOTEST&javaVersion=JDK_21' --output demo.zip

Version

4.3.7

sgammon commented 8 months ago

Hey @erkieh,

I am not a Micronaut project contributor, but I can probably still help you out with this. GraalVM performs class initialization at either build time or run time. Which classes are loaded and in what order depends on each application, of course, so these errors vary quite a bit and may surface in Launch-generated samples.

It is frequent to run into these errors when building a native app with GraalVM, and it isn't always immediately clear what to do, but there are some ways to figure it out. Here are the steps I take, which nearly always work eventually:

1) Mark the class as --initialize-at-build-time If you pass --initialize-at-build-time=<class> during the build, to native-image, you are telling the Native Image builder that the subject class is OK to initialize at build time, and this alone will often clear up the error, so long as the class is sensible to initialize at build time in the first place, which isn't always true. In that case, you can...

2) Mark the class as --initialize-at-run-time If you pass --initialize-at-run-time<class>, native-image will try to defer initialization for that class and any related necessary classes. For example, say you have a class which uses epoll on Linux; epoll may not always be available, and you may want to fall back to NIO. You could initialize such a class at runtime to avoid the build-time environment from governing whether support is detected.

3) Run with --trace-object-instantiation When you're not sure which of the above to use for a given class, you can use --trace-object-instantiation=<class>, which will print more information about why the class in question was instantiated at build time.

What I see from your log is that several Logback classes are showing up as initialized at build time, which makes sense: logback.xml is loaded early in the program lifecycle. There are some Netty classes too. If you have trouble figuring out the right flags to use, I can try to dig up one of the cases where we also encountered these and see what flags made it work.

erkieh commented 8 months ago

Hi @sgammon Thanks for replying. I did try creating a reflect-config.json in a real project with the help of trace flags, but it felt like a rabbit hole with no bottom. It would be interesting to see what got it working in your case.

graemerocher commented 8 months ago

This has to be fixed upstream in native image.

fniephaus commented 6 months ago

I've created an internal tracking ticket (GR-54365) for this.