quarkus
quarkus copied to clipboard
Significant increase of Quarkus compiled binary after upgrade from 3.6.7 to 3.7.1
Discussed in https://github.com/quarkusio/quarkus/discussions/38572
Originally posted by vincejv February 3, 2024 Any one noticed a significant increase in the binaries compiled with 3.7.1 compared to 3.6.7? I'm using few Quarkus libraries on top of the framework, mostly just rest client related, and I have observed the docker image published in docker hub increased by 10mb+ (https://hub.docker.com/r/vincejv/qbittorrent-exporter/tags) 0.0.18 (37mb) vs 0.0.17 (28mb).
I thought removing support for older java version would actually reduce the binaries as it will remove legacy code?
Source code: https://github.com/vincejv/qbittorrent-exporter
Reproducer
git clone https://github.com/vincejv/qbittorrent-exporter
cd qbittorrent-exporter
git worktree add ../qbittorrent-exporter-3.6
./gradlew build -Dquarkus.package.type=native
cd ../qbittorrent-exporter-3.6
sed -i 's/3\.7\.1/3.6.7/g' gradle.properties
./gradlew build -Dquarkus.package.type=native -D
Build output
3.6.7
Warning: The option '-H:ReflectionConfigurationResources=META-INF/native-image/io.micrometer/micrometer-core/reflect-config.json' is experimental and must be enabled via '-H:+UnlockExperimentalVMOptions' in the future.
Warning: The option '-H:ReflectionConfigurationResources=META-INF/native-image/io.netty/netty-transport/reflection-config.json' is experimental and must be enabled via '-H:+UnlockExperimentalVMOptions' in the future.
Warning: Please re-evaluate whether any experimental option is required, and either remove or unlock it. The build output lists all active experimental options, including where they come from and possible alternatives. If you think an experimental option should be considered as stable, please file an issue.
========================================================================================================================
GraalVM Native Image: Generating 'qbittorrent-exporter-unspecified-runner' (executable)...
========================================================================================================================
For detailed information and explanations on the build output, visit:
https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/BuildOutput.md
------------------------------------------------------------------------------------------------------------------------
[1/8] Initializing... (1.9s @ 0.26GB)
Java version: 21.0.1+12-LTS, vendor version: Mandrel-23.1.1.0-Final
Graal compiler: optimization level: 2, target machine: compatibility
C compiler: gcc (redhat, x86_64, 13.2.1)
Garbage collector: Serial GC (max heap size: 80% of RAM)
3 user-specific feature(s):
- com.oracle.svm.thirdparty.gson.GsonFeature
- io.quarkus.runner.Feature: Auto-generated class by Quarkus from the existing extensions
- io.quarkus.runtime.graal.DisableLoggingFeature: Disables INFO logging during the analysis phase
------------------------------------------------------------------------------------------------------------------------
4 experimental option(s) unlocked:
- '-H:+AllowFoldMethods' (origin(s): command line)
- '-H:BuildOutputJSONFile' (origin(s): command line)
- '-H:-UseServiceLoaderFeature' (origin(s): command line)
- '-H:ReflectionConfigurationResources' (origin(s): 'META-INF/native-image/io.micrometer/micrometer-core/native-image.properties' in 'file:///home/zakkak/code/tmp/qbittorrent-exporter/build/qbittorrent-exporter-unspecified-native-image-source-jar/lib/io.micrometer.micrometer-core-1.11.5.jar', 'META-INF/native-image/io.netty/netty-transport/native-image.properties' in 'file:///home/zakkak/code/tmp/qbittorrent-exporter/build/qbittorrent-exporter-unspecified-native-image-source-jar/lib/io.netty.netty-transport-4.1.100.Final.jar')
------------------------------------------------------------------------------------------------------------------------
Build resources:
- 17.47GB of memory (28.2% of 61.94GB system memory, determined at start)
- 32 thread(s) (100.0% of 32 available processor(s), determined at start)
[2/8] Performing analysis... [*****] (6.4s @ 1.37GB)
12,164 reachable types (86.5% of 14,059 total)
17,966 reachable fields (59.3% of 30,287 total)
60,966 reachable methods (57.9% of 105,323 total)
3,762 types, 345 fields, and 3,934 methods registered for reflection
61 types, 59 fields, and 55 methods registered for JNI access
4 native libraries: dl, pthread, rt, z
[3/8] Building universe... (1.6s @ 1.48GB)
[4/8] Parsing methods... [*] (0.8s @ 1.58GB)
[5/8] Inlining methods... [***] (0.4s @ 1.52GB)
[6/8] Compiling methods... [**] (4.3s @ 2.01GB)
[7/8] Layouting methods... [**] (2.3s @ 1.56GB)
[8/8] Creating image... [**] (2.7s @ 2.07GB)
24.84MB (46.91%) for code area: 38,892 compilation units
27.71MB (52.34%) for image heap: 319,952 objects and 83 resources
406.17kB ( 0.75%) for other data
52.95MB in total
------------------------------------------------------------------------------------------------------------------------
Top 10 origins of code area: Top 10 object types in image heap:
12.69MB java.base 7.87MB byte[] for code metadata
1.35MB java.net.http 4.17MB byte[] for java.lang.String
1.35MB svm.jar (Native Image) 2.98MB java.lang.Class
997.90kB modified-io.vertx.vertx-core-4.4.6.jar 2.89MB java.lang.String
981.48kB qbittorrent-exporter-unspecified-runner.jar 1.03MB byte[] for general heap data
575.14kB org.yaml.snakeyaml-2.2.jar 1.02MB com.oracle.svm.core.hub.DynamicHubCompanion
498.01kB io.netty.netty-buffer-4.1.100.Final.jar 737.07kB byte[] for reflection metadata
483.75kB io.netty.netty-common-4.1.100.Final.jar 571.48kB java.lang.String[]
418.46kB io.netty.netty-codec-http-4.1.100.Final.jar 442.42kB c.o.svm.core.hub.DynamicHub$ReflectionMetadata
380.80kB io.netty.netty-codec-http2-4.1.100.Final.jar 427.66kB java.lang.Object[]
4.94MB for 85 more packages 5.62MB for 2994 more object types
------------------------------------------------------------------------------------------------------------------------
Recommendations:
HEAP: Set max heap for improved and more predictable memory usage.
------------------------------------------------------------------------------------------------------------------------
1.5s (7.2% of total time) in 118 GCs | Peak RSS: 3.30GB | CPU load: 15.27
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:
/home/zakkak/code/tmp/qbittorrent-exporter/build/qbittorrent-exporter-unspecified-native-image-source-jar/qbittorrent-exporter-unspecified-runner (executable)
/home/zakkak/code/tmp/qbittorrent-exporter/build/qbittorrent-exporter-unspecified-native-image-source-jar/qbittorrent-exporter-unspecified-runner-build-output-stats.json (build_info)
========================================================================================================================
Finished generating 'qbittorrent-exporter-unspecified-runner' in 20.8s.
3.7.1
Your application is setting the 'quarkus.locales' configuration key to include 'all'. All JDK locales, languages, currencies, etc. will be included, inflating the size of the executable.
========================================================================================================================
GraalVM Native Image: Generating 'qbittorrent-exporter-unspecified-runner' (executable)...
========================================================================================================================
For detailed information and explanations on the build output, visit:
https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/BuildOutput.md
------------------------------------------------------------------------------------------------------------------------
[1/8] Initializing... (1.9s @ 0.27GB)
Java version: 21.0.1+12-LTS, vendor version: Mandrel-23.1.1.0-Final
Graal compiler: optimization level: 2, target machine: compatibility
C compiler: gcc (redhat, x86_64, 13.2.1)
Garbage collector: Serial GC (max heap size: 80% of RAM)
3 user-specific feature(s):
- com.oracle.svm.thirdparty.gson.GsonFeature
- io.quarkus.runner.Feature: Auto-generated class by Quarkus from the existing extensions
- io.quarkus.runtime.graal.DisableLoggingFeature: Disables INFO logging during the analysis phase
------------------------------------------------------------------------------------------------------------------------
4 experimental option(s) unlocked:
- '-H:+IncludeAllLocales' (origin(s): command line)
- '-H:+AllowFoldMethods' (origin(s): command line)
- '-H:BuildOutputJSONFile' (origin(s): command line)
- '-H:-UseServiceLoaderFeature' (origin(s): command line)
------------------------------------------------------------------------------------------------------------------------
Build resources:
- 18.13GB of memory (29.3% of 61.94GB system memory, determined at start)
- 32 thread(s) (100.0% of 32 available processor(s), determined at start)
[2/8] Performing analysis... [*****] (6.9s @ 1.50GB)
13,994 reachable types (88.1% of 15,893 total)
18,053 reachable fields (59.4% of 30,383 total)
64,721 reachable methods (58.3% of 111,023 total)
27,459 types, 345 fields, and 5,737 methods registered for reflection
61 types, 59 fields, and 55 methods registered for JNI access
4 native libraries: dl, pthread, rt, z
[3/8] Building universe... (1.9s @ 1.62GB)
[4/8] Parsing methods... [*] (0.9s @ 1.91GB)
[5/8] Inlining methods... [***] (0.4s @ 1.28GB)
[6/8] Compiling methods... [***] (5.2s @ 1.91GB)
[7/8] Layouting methods... [**] (2.2s @ 1.45GB)
[8/8] Creating image... [**] (3.9s @ 2.21GB)
25.38MB (34.34%) for code area: 40,769 compilation units
48.12MB (65.12%) for image heap: 616,599 objects and 21,975 resources
405.39kB ( 0.54%) for other data
73.89MB in total
------------------------------------------------------------------------------------------------------------------------
Top 10 origins of code area: Top 10 object types in image heap:
12.70MB java.base 9.45MB byte[] for java.lang.String
1.71MB svm.jar (Native Image) 8.00MB byte[] for code metadata
1.35MB java.net.http 6.27MB byte[] for general heap data
1.07MB qbittorrent-exporter-unspecified-runner.jar 5.58MB java.lang.String
1018.87kB modified-io.vertx.vertx-core-4.5.1.jar 3.49MB java.lang.Class
575.06kB org.yaml.snakeyaml-2.2.jar 2.24MB java.util.concurrent.ConcurrentHashMap$Node
500.26kB io.netty.netty-buffer-4.1.106.Final.jar 2.09MB java.lang.Object[]
490.29kB io.netty.netty-common-4.1.106.Final.jar 1.17MB com.oracle.svm.core.hub.DynamicHubCompanion
420.50kB io.netty.netty-codec-http-4.1.106.Final.jar 860.11kB byte[] for reflection metadata
382.09kB io.netty.netty-codec-http2-4.1.106.Final.jar 858.40kB org.graalvm.collections.Pair
4.95MB for 84 more packages 8.14MB for 2999 more object types
------------------------------------------------------------------------------------------------------------------------
Recommendations:
HEAP: Set max heap for improved and more predictable memory usage.
------------------------------------------------------------------------------------------------------------------------
2.2s (8.0% of total time) in 119 GCs | Peak RSS: 4.19GB | CPU load: 13.80
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:
/home/zakkak/code/tmp/qbittorrent-exporter/build/qbittorrent-exporter-unspecified-native-image-source-jar/qbittorrent-exporter-unspecified-runner (executable)
/home/zakkak/code/tmp/qbittorrent-exporter/build/qbittorrent-exporter-unspecified-native-image-source-jar/qbittorrent-exporter-unspecified-runner-build-output-stats.json (build_info)
========================================================================================================================
Finished generating 'qbittorrent-exporter-unspecified-runner' in 27.8s.
Observations
- The binary size increases from 52.95MB to 73.89MB (3.6.7 vs 3.7.1)
- The build process in
3.7.1
mentions that:
Your application is setting the 'quarkus.locales' configuration key to include 'all'. All JDK locales, languages, currencies, etc. will be included, inflating the size of the executable.
which is known to increase the binary size.
Test
Changing quarkus.locales
to en-US
in src/main/resources/application.yaml
and rebuilding results in a binary size equal to 53.14MB.
quarkus.locales=all
support was added in 3.7 by https://github.com/quarkusio/quarkus/pull/37106, prior to that setting the locales to all
probably did nothing (it resulted in -H:IncludeLocales=all
being passed to native-image
which AFAIK will silently fail to detect the all
locale and will thus do nothing.)
cc @Karm
The binary size increases from 52.95MB to 73.89MB (3.6.7 vs 3.7.1)
This is pretty bad if you ask me :)
The binary size increases from 52.95MB to 73.89MB (3.6.7 vs 3.7.1)
This is pretty bad if you ask me :)
@geoand this is not the default behavior though. This is only happening because the user is requesting all locales to be included and it's documented in https://github.com/Karm/quarkus/blob/c025a1b29c02b6a4b8b773d6041c9e50119147a0/docs/src/main/asciidoc/validation.adoc?plain=1#L400-L401
Oh, then I misread the investigation.
Thanks for the clarification!
I think we can close this issue, thanks @zakkak .
Is this actually closed? I'm still seeing large native binary sizes (52.7MB) when:
Quarkus 3.7.1
quarkus create cd ./code-with-quarkus quarkus build --native
application.properties: quarkus.locales=en-US
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Running Quarkus native-image plugin on GRAALVM 23.1 JDK 21.0.1+12-jvmci-23.1-b19
52 MB for a basic application looks like a lot.
I think we need some further investigation.
50+ MB
for a mostly empty application with only RESTEasy Reactive around is too much IMHO. I think we were a lot better before.
... digging ...
The same app created with Quarkus 2.16 is 40 MB
. I also tested 2.13 with the same result.
I also did an experiment with Quarkus 1.13.3.Final and RESTEasy Classic (not RESTEasy Reactive at first as I wasn't sure it was already there) and I ended up with a 29 MB binary. Switch to RESTEasy Reactive again with 1.13 and got 31 MB.
So we basically have our default REST app going like that:
Version | Size in MB |
---|---|
1.13 with RESTEasy Classic | 29 |
1.13 with RESTEasy Reactive | 31 |
2.16 with RESTEasy Reactive | 40 |
3.7 with RESTEasy Reactive | 52 |
This is not looking good.
/cc @maxandersen @geoand @cescoffier @zakkak
Do you also have numbers for 3.2?
If I am not mistaken
2.16 is using GraalVM/Mandrel 22.3 3.2 is using GraalVM/Mandrel 23.0 3.7 is using GraalVM/Mandrel 23.1
In each of these updates we know that GraalVM/Mandrel increased the resulting binary size:
- https://quarkus.io/blog/mandrel-23-0-image-size-increase/
- https://quarkus.io/blog/mandrel-23-1-image-size-increase/
It's also worth keeping in mind that if the runtime performance is not impacted by these changes, while they bring some improvements in other aspects (like stability, debugging, etc.), the binary size shouldn't matter that much (at least in the common case).
Here are some new data points obtained with:
- the Quarkus default app obtained by using
quarkus create app
(but making sure we use RESTEasy Reactive as we changed the default at some point, except for the first number which is basically the baseline I still had in mind...) - normalized with a container build (not sure why but I obtain 52 for 3.7 when building with a local GraalVM)
Version | Size in MB |
---|---|
1.13 with RESTEasy Classic | 29 |
1.13 with RESTEasy Reactive | 31 |
2.0 with RESTEasy Reactive | 39 |
2.1 with RESTEasy Reactive | 40 |
2.16 with RESTEasy Reactive | 40 |
3.0 with RESTEasy Reactive | 42 |
3.1 with RESTEasy Reactive | 42 |
3.2 with RESTEasy Reactive | 44 |
3.3 with RESTEasy Reactive | 44 |
3.4 with RESTEasy Reactive | 44 |
3.5 with RESTEasy Reactive | 47 |
3.6 with RESTEasy Reactive | 47 |
3.7 with RESTEasy Reactive | 48 |
It's also worth keeping in mind that if the runtime performance is not impacted by these changes, while they bring some improvements in other aspects (like stability, debugging, etc.), the binary size shouldn't matter that much (at least in the common case).
I dunno, I can't say I'm excited about a 65% increase in binary size for a very simple app.
@gsmet maybe you can add the graalVM version you used for each entry.
The reason is that without large changes (so 3.x) we have a bump of +2 Mb between 3.1 and 3.2 and +# Mb between 3.4 and 3.5...
a picture shows it better ..
so its not a massive jump from 3.6 to 3.7 here at least? its more 3.4 to 3.5?
similar issue in https://github.com/quarkusio/quarkus/issues/38683 but more affecting time.
@gsmet maybe you can add the graalVM version you used for each entry.
Assuming that @gsmet used the default builder image of each version the table with the Mandrel/GraalVM version is:
Version | Mandrel/GraalVM Version | Size in MB |
---|---|---|
1.13 with RESTEasy Classic | GraalVM CE 21.0 - Java 11 | 29 |
1.13 with RESTEasy Reactive | GraalVM CE 21.0 - Java 11 | 31 |
2.0 with RESTEasy Reactive | GraalVM CE 21.1 - Java 11 | 39 |
2.1 with RESTEasy Reactive | GraalVM CE 21.0 - Java 11 | 40 |
2.16 with RESTEasy Reactive | Mandrel 22.3 - Java 17 | 40 |
3.0 with RESTEasy Reactive | Mandrel 22.3 - Java 17 | 42 |
3.1 with RESTEasy Reactive | Mandrel 22.3 - Java 17 | 42 |
3.2 with RESTEasy Reactive | Mandrel 23.0 - Java 17 | 44 |
3.3 with RESTEasy Reactive | Mandrel 23.0 - Java 17 | 44 |
3.4 with RESTEasy Reactive | Mandrel 23.0 - Java 17 | 44 |
3.5 with RESTEasy Reactive | Mandrel 23.1 - Java 21 | 47 |
3.6 with RESTEasy Reactive | Mandrel 23.1 - Java 21 | 47 |
3.7 with RESTEasy Reactive | Mandrel 23.1 - Java 21 | 48 |
so its not a massive jump from 3.6 to 3.7 here at least? its more 3.4 to 3.5?
Correct, we should either move this discussion in a different issue or edit the title (since we are now discussing a different thing than the issue reported originally).
Now regarding the increases between 3.1 and 3.2, and later between 3.4 and 3.5 one can read https://quarkus.io/blog/mandrel-23-0-image-size-increase/ and https://quarkus.io/blog/mandrel-23-1-image-size-increase/ (at least for the part that Mandrel/GraalVM is responsible for).