framework-benchmarks: Spring Boot 4.1.1 vs Quarkus 3.40.1 vs Micronaut 5.2.2, JVM + native, benchmarked
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
Micronaut native-image build: the three failed attempts before the fix, verbatim
|
||||
error text from the real `native-image` invocations (trimmed to the diagnostic
|
||||
message; the full stack scan is in each build's svm_err_*.md, not committed since
|
||||
it also repeats the full classpath).
|
||||
|
||||
=== Attempt 1 (no --initialize-at-build-time / --initialize-at-run-time args at all) ===
|
||||
Failed generating 'micronaut-app' after 42.4s.
|
||||
|
||||
> com.oracle.graal.pointsto.util.ParallelExecutionException: An object of type 'ch.qos.logback.classic.Logger' was 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.
|
||||
This is not allowed for correctness reasons: All objects that are stored in the image heap must be initialized at build time.
|
||||
...
|
||||
Object was reached by
|
||||
scanning root constant ch.qos.logback.classic.Logger@7f585330: Logger[io.netty.util.ResourceLeakDetector] embedded in
|
||||
<unknown>
|
||||
at io.netty.util.ResourceLeakDetector.needReport() [bci: -5]
|
||||
|
||||
=== Attempt 2 (--initialize-at-build-time=ch.qos.logback.classic.Logger) ===
|
||||
Failed generating 'micronaut-app' after 46.0s.
|
||||
|
||||
> com.oracle.graal.pointsto.util.ParallelExecutionException: An object of type 'ch.qos.logback.classic.LoggerContext' was found in the image heap. This type, however, is marked for initialization at image run time ...
|
||||
Object was reached by
|
||||
reading field ch.qos.logback.classic.Logger.loggerContext of constant
|
||||
ch.qos.logback.classic.Logger@6f5cb3e9: Logger[io.netty.util.ResourceLeakDetector]
|
||||
scanning root constant ch.qos.logback.classic.Logger@6f5cb3e9: Logger[io.netty.util.ResourceLeakDetector] embedded in
|
||||
<unknown>
|
||||
at io.netty.util.ResourceLeakDetector.needReport() [bci: -5]
|
||||
|
||||
=== Attempt 3 (--initialize-at-build-time=ch.qos.logback [whole package]) ===
|
||||
Failed generating 'micronaut-app' after 49.9s.
|
||||
|
||||
> com.oracle.graal.pointsto.util.ParallelExecutionException: An object of type 'ch.qos.logback.classic.Logger' was found in the image heap. This type, however, is marked for initialization at image run time ...
|
||||
(identical trace to Attempt 1 - the package-level directive lost to a more specific
|
||||
--initialize-at-run-time directive for this exact class shipped in logback-classic's
|
||||
own META-INF/native-image/*/native-image.properties: GraalVM lets a more specific
|
||||
per-class directive override a broader package-level one, in either direction.)
|
||||
|
||||
=== Attempt 4 (--initialize-at-run-time=io.netty.util.ResourceLeakDetector,io.netty.util.ResourceLeakDetectorFactory) ===
|
||||
Failed generating 'micronaut-app' after 42.9s.
|
||||
|
||||
> com.oracle.graal.pointsto.util.ParallelExecutionException: An object of type 'io.netty.util.ResourceLeakDetector' was found in the image heap. This type, however, is marked for initialization at image run time ...
|
||||
Object was reached by
|
||||
scanning root constant io.netty.util.ResourceLeakDetector@2a2de2c0: io.netty.util.ResourceLeakDetector@2a2de2c0 embedded in
|
||||
io.netty.buffer.AbstractByteBufAllocator.toLeakAwareBuffer(AbstractByteBufAllocator.java:53)
|
||||
parsing method io.netty.buffer.AbstractByteBufAllocator.toLeakAwareBuffer(AbstractByteBufAllocator.java:53) reachable via the parsing context
|
||||
at static root method.(Unknown Source)
|
||||
(deferring just the leak detector to run-time wasn't enough: AbstractByteBufAllocator,
|
||||
itself build-time-initialized by default, holds a static reference to one.)
|
||||
|
||||
=== Fix that worked (--initialize-at-run-time=io.netty [whole package]) ===
|
||||
Finished generating 'micronaut-app' in 7m 15s.
|
||||
2,621 types -> 3,395 types reachable after the fix (more code stays reachable because
|
||||
initialization now genuinely happens at run time instead of being folded away), image
|
||||
heap 854MB peak RSS during the build, final executable 53.31MiB.
|
||||
|
||||
Takeaway for the post: this is a build-time ordering conflict between two framework
|
||||
defaults, not a bug in either library - Netty wants its leak detector and buffer
|
||||
allocator built eagerly (for speed), logback wants its Logger/LoggerContext run-time
|
||||
(so file handles and config aren't baked into the image), and the two defaults only
|
||||
collide when both are on the classpath AND something (here, static root scanning
|
||||
during Micronaut's own analysis) forces Netty's allocator to be build-time reachable.
|
||||
Pushing the whole io.netty package to run-time init was the fix that actually stuck;
|
||||
chasing individual classes one GraalVM error message at a time was not.
|
||||
Reference in New Issue
Block a user