Skip to content

Commit 890a0ed

Browse files
committed
[GR-69576] Use KiB MiB GiB consistently in Native Image output
PullRequest: graal/22104
2 parents 1cde159 + 6380ebb commit 890a0ed

File tree

5 files changed

+113
-64
lines changed

5 files changed

+113
-64
lines changed

docs/reference-manual/native-image/BuildOutput.md

Lines changed: 57 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,27 @@ redirect_from: /reference-manual/native-image/BuildOutput/
1919
Here you will find information about the build output of GraalVM Native Image.
2020
Below is the example output when building a native executable of the `HelloWorld` class:
2121

22+
23+
<!--
24+
To update the output below:
25+
$ cd substratevm-enterprise
26+
Ensure to set JAVA_HOME to a labsjdk-ee
27+
$ mx build
28+
$ (stty cols 80 && mx helloworld -g)
29+
Remove the `experimental option(s)` section
30+
Remove the `# Printing compilation-target` and `# Printing native-library` lines
31+
Replace the absolute paths at the end to start with `/home/janedoe/helloworld`
32+
-->
33+
2234
```
2335
================================================================================
2436
GraalVM Native Image: Generating 'helloworld' (executable)...
2537
================================================================================
26-
[1/8] Initializing... (2.0s @ 0.19GB)
38+
[1/8] Initializing... (4.4s @ 0.29GiB)
2739
Builder configuration:
28-
- Java version: 26+12, vendor version: GraalVM CE 26-dev+12.1
29-
- Graal compiler: optimization level: 2, target machine: x86-64-v3
30-
- C compiler: gcc (linux, x86_64, 15.2.1)
40+
- Java version: 26+13, vendor version: Oracle GraalVM 26-dev+13.1
41+
- Graal compiler: optimization level: 2, target machine: x86-64-v3, PGO: ML-inferred
42+
- C compiler: gcc (linux, x86_64, 13.3.0)
3143
- Assertions: enabled, system assertions: enabled
3244
- 1 user-specific feature(s):
3345
- com.oracle.svm.thirdparty.gson.GsonFeature
@@ -36,53 +48,61 @@ GraalVM Native Image: Generating 'helloworld' (executable)...
3648
- Assertions: disabled (class-specific config may apply), system assertions: disabled
3749
--------------------------------------------------------------------------------
3850
Build resources:
39-
- 14.69GiB of memory (47.0% of system memory, using all available memory)
40-
- 20 thread(s) (100.0% of 20 available processor(s), determined at start)
41-
[2/8] Performing analysis... [******] (3.4s @ 0.40GB)
42-
3,297 types, 3,733 fields, and 15,247 methods found reachable
43-
1,066 types, 36 fields, and 415 methods registered for reflection
44-
58 types, 59 fields, and 52 methods registered for JNI access
45-
0 downcalls and 0 upcalls registered for foreign access
51+
- 30.00GiB of memory (48.0% of system memory, capped at 30GiB)
52+
- 32 thread(s) (88.9% of 36 available processor(s), determined at start)
53+
[2/8] Performing analysis... [*******] (3.7s @ 0.58GiB)
54+
2,140 types, 1,939 fields, and 8,997 methods found reachable
55+
775 types, 35 fields, and 244 methods registered for reflection
56+
49 types, 35 fields, and 48 methods registered for JNI access
57+
52 resource accesses registered with 107B total size
4658
4 native libraries: dl, pthread, rt, z
47-
[3/8] Building universe... (1.0s @ 0.60GB)
48-
[4/8] Parsing methods... [*] (0.4s @ 0.62GB)
49-
[5/8] Inlining methods... [****] (0.2s @ 0.59GB)
50-
[6/8] Compiling methods... [**] (3.7s @ 0.66GB)
51-
[7/8] Laying out methods... [*] (0.7s @ 0.60GB)
52-
[8/8] Creating image... [**] (2.3s @ 0.65GB)
53-
5.24MB (21.86%) for code area: 8,788 compilation units
54-
7.67MB (32.01%) for image heap: 90,323 objects and 55 resources
55-
9.43MB (39.34%) for debug info generated in 0.3s
56-
11.05MB (46.13%) for other data
57-
23.96MB in total image size, 13.31MB in total file size
59+
[3/8] Building universe... (0.9s @ 0.74GiB)
60+
[4/8] Parsing methods... [*] (1.6s @ 0.72GiB)
61+
[5/8] Inlining methods... [***] (0.5s @ 0.66GiB)
62+
[6/8] Compiling methods... [***] (9.9s @ 0.81GiB)
63+
[7/8] Laying out methods... [*] (1.1s @ 0.67GiB)
64+
[8/8] Creating image... [**] (2.5s @ 0.90GiB)
65+
2.86MiB (21.13%) for code area: 4,078 compilation units
66+
3.56MiB (26.33%) for image heap: 63,478 objects and 1 resource
67+
6.01MiB (44.40%) for debug info generated in 0.4s
68+
7.11MiB (52.55%) for other data
69+
13.53MiB in total image size, 6.88MiB in total file size
5870
--------------------------------------------------------------------------------
5971
Top 10 origins of code area: Top 10 object types in image heap:
60-
791.32kB java.base/java.util 1.41MB byte[] for code metadata
61-
363.66kB java.base/java.lang 1.21MB byte[] for string data
62-
323.39kB java.base/java.text 838.53kB java.base/java.lang.String
63-
241.87kB java.base/java.util.stream 633.02kB o.g.n.~e/c.o.s.c.h.Dyna~anion
64-
229.23kB java.base/java.util.regex 431.58kB heap alignment
65-
214.23kB java.base/java.util.concurrent 428.26kB java.base/java.lang.Class
66-
166.60kB o.g.n.~e/c.o.svm.core.code 323.23kB java.base/j.util.HashMap$Node
67-
153.78kB java.base/java.time.format 284.47kB byte[] for general heap data
68-
152.90kB java.base/java.math 232.06kB java.base/java.lang.Object[]
69-
142.02kB o.g.n.~e/c.o.s.c.genscavenge 183.10kB java.base/j.u.HashMap$Node[]
70-
2.32MB for 146 more packages 1.70MB for 966 more object types
72+
342.94KiB java.base/java.util 820.99KiB byte[] for string data
73+
289.94KiB java.base/java.lang 750.97KiB byte[] for code metadata
74+
270.50KiB o.g.n.~e/c.o.svm.core.code 347.48KiB java.base/java.lang.String
75+
189.67KiB o.g.n.~e/c.o.s.c.genscavenge 217.34KiB o.g.n.~e/c.o.s.c.h.Dyna~anion
76+
129.09KiB java.base/j.util.concurrent 209.51KiB java.base/java.lang.Class
77+
83.00KiB o.g.n.~e/c.o.s.c.j.functions 184.75KiB java.base/j.u.HashMap$Node
78+
81.76KiB java.base/java.util.stream 115.53KiB java.base/char[]
79+
76.73KiB o.g.n.~e/com.oracle.svm.core 107.66KiB java.base/j.i.u.SoftR~nceKey
80+
60.32KiB o.g.n.~e/c.o.svm.core.thread 105.09KiB java.base/java.lang.Object[]
81+
58.18KiB o.g.n.~e/c.o.svm.graal.stubs 88.63KiB java.base/j.u.c.Concu~p$Node
82+
1.25MiB for 119 more packages 700.05KiB for 585 more object types
83+
Use '--emit build-report' to create a report with more details.
84+
--------------------------------------------------------------------------------
85+
Security report:
86+
- Binary includes Java deserialization.
87+
- CycloneDX SBOM with 5 component(s) is embedded in binary (406B). 6 type(s) could not be associated to a component.
88+
- Advanced obfuscation not enabled; enable with '-H:AdvancedObfuscation=""' (experimental support).
7189
--------------------------------------------------------------------------------
7290
Recommendations:
91+
G1GC: Use the G1 GC ('--gc=G1') for improved latency and throughput.
92+
PGO: Use Profile-Guided Optimizations ('--pgo') for improved throughput.
7393
FUTR: Use '--future-defaults=all' to prepare for future releases.
7494
HEAP: Set max heap for improved and more predictable memory usage.
7595
CPU: Enable more CPU features with '-march=native' for improved performance.
7696
--------------------------------------------------------------------------------
77-
0.9s (6.1% of total time) in 54 GCs | Peak RSS: 1.82GB | CPU load: 13.25
97+
1.3s (4.8% of total time) in 88 GCs | Peak RSS: 2.14GiB | CPU load: 18.03
7898
--------------------------------------------------------------------------------
7999
Build artifacts:
80100
/home/janedoe/helloworld/gdb-debughelpers.py (debug_info)
81101
/home/janedoe/helloworld/helloworld (executable)
82102
/home/janedoe/helloworld/helloworld.debug (debug_info)
83103
/home/janedoe/helloworld/sources (debug_info)
84104
================================================================================
85-
Finished generating 'helloworld' in 14.2s.
105+
Finished generating 'helloworld' in 25.5s.
86106
```
87107

88108
## Build Stages
@@ -160,9 +180,9 @@ The memory limit and number of threads used by the build process.
160180
More precisely, the memory limit of the Java heap, so actual memory consumption can be higher.
161181
Please check the [peak RSS](#glossary-peak-rss) reported at the end of the build to understand how much memory was actually used.
162182
The actual memory consumption can also be lower than the limit set, as the GC only commits memory that it needs.
163-
By default, the build process uses the dedicated mode (which uses 85% of system memory) in containers or CI environments (when the `$CI` environment variable is set to `true`), but never more than 32GB of memory.
183+
By default, the build process uses the dedicated mode (which uses 85% of system memory) in containers or CI environments (when the `$CI` environment variable is set to `true`), but never more than 30GiB of memory.
164184
Otherwise, it uses shared mode, which uses the available memory to avoid memory pressure on developer machines.
165-
If less than 8GB of memory are available, the build process falls back to the dedicated mode.
185+
If less than 8GiB of memory are available, the build process falls back to the dedicated mode.
166186
Therefore, consider freeing up memory if your machine is slow during a build, for example, by closing applications that you do not need.
167187
It is possible to override the default behavior and set relative or absolute memory limits, for example with `-J-XX:MaxRAMPercentage=60.0` or `-J-Xmx16g`.
168188
`Xms` (for example, `-J-Xms9g`) can also be used to ensure a minimum for the limit, if you know the image needs at least that much memory to build.

substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/MemoryUtil.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,13 @@ public final class MemoryUtil {
6363
private static final int MIN_AVAILABLE_MEMORY_THRESHOLD_GB = 8;
6464

6565
/*
66-
* Builder uses at most 32GB to avoid disabling compressed oops (UseCompressedOops).
67-
* Deliberately use GB (not GiB) to stay well below 32GiB when relative maximum is calculated.
66+
* Builder uses at most 30GiB to avoid disabling compressed oops (UseCompressedOops).
67+
* UseCompressedOops seems currently disabled on my machine at values > (32 GiB - 32 MiB), so we
68+
* use 30 GiB to have some margin. The actual logic at
69+
* https://github.com/openjdk/jdk/blob/jdk-26+10/src/hotspot/share/runtime/arguments.cpp#L1429
70+
* depends on various factors so it seems unwise to hardcode that exact limit.
6871
*/
69-
public static final long MAX_HEAP_BYTES = 32_000_000_000L;
72+
public static final long MAX_HEAP_BYTES = 30 * GiB_TO_BYTES;
7073

7174
public static List<String> heuristicMemoryFlags(HostFlags hostFlags, List<String> memoryFlags) {
7275
/*
@@ -146,7 +149,7 @@ public static Pair<Long, String> maxMemoryHeuristic(long totalMemorySize, boolea
146149
// Ensure max memory size does not exceed upper limit
147150
if (maxMemory > MAX_HEAP_BYTES) {
148151
maxMemory = MAX_HEAP_BYTES;
149-
reason = percentageOfSystemMemoryText(maxMemory, totalMemorySize) + ", capped at 32GB";
152+
reason = percentageOfSystemMemoryText(maxMemory, totalMemorySize) + ", capped at 30GiB";
150153
}
151154

152155
// Handle memory flags

substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,8 @@ public record HostFlags(
351351
boolean hasGCTimeRatio,
352352
boolean hasExitOnOutOfMemoryError,
353353
boolean hasMaximumHeapSizePercent,
354-
boolean hasUseParallelGC) {
354+
boolean hasUseParallelGC,
355+
boolean hasUseCompressedOops) {
355356

356357
public List<String> defaultMemoryFlags() {
357358
List<String> flags = new ArrayList<>();
@@ -372,6 +373,12 @@ public List<String> defaultMemoryFlags() {
372373
*/
373374
flags.add("-XX:+ExitOnOutOfMemoryError");
374375
}
376+
if (hasUseCompressedOops) {
377+
/*
378+
* To print a warning if the max heap size is too large for compressed oops.
379+
*/
380+
flags.add("-XX:+UseCompressedOops");
381+
}
375382
return flags;
376383
}
377384
}
@@ -574,6 +581,7 @@ private HostFlags gatherHostFlags() {
574581
boolean hasGCTimeRatio = false;
575582
boolean hasExitOnOutOfMemoryError = false;
576583
boolean hasUseParallelGC = false;
584+
boolean hasUseCompressedOops = false;
577585

578586
ProcessBuilder pb = new ProcessBuilder();
579587
sanitizeJVMEnvironment(pb.environment(), Map.of());
@@ -604,6 +612,8 @@ private HostFlags gatherHostFlags() {
604612
hasMaximumHeapSizePercent = true;
605613
} else if (line.contains(" UseParallelGC ")) {
606614
hasUseParallelGC = true;
615+
} else if (line.contains(" UseCompressedOops ")) {
616+
hasUseCompressedOops = true;
607617
}
608618
}
609619
}
@@ -623,7 +633,8 @@ private HostFlags gatherHostFlags() {
623633
hasGCTimeRatio,
624634
hasExitOnOutOfMemoryError,
625635
hasMaximumHeapSizePercent,
626-
hasUseParallelGC);
636+
hasUseParallelGC,
637+
hasUseCompressedOops);
627638
}
628639

629640
/**

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ByteFormattingUtil.java

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,33 +25,45 @@
2525
package com.oracle.svm.hosted;
2626

2727
public class ByteFormattingUtil {
28-
private static final double BYTES_TO_KB = 1000d;
29-
private static final double BYTES_TO_MB = 1000d * 1000d;
30-
private static final double BYTES_TO_GB = 1000d * 1000d * 1000d;
28+
// "123.12KiB".length() = 9, holds as long as it's not >= 1000GiB
29+
private static final int MAX_WIDTH = 9;
30+
public static final String RIGHT_ALIGNED_FORMAT = "%" + MAX_WIDTH + "s";
3131

32+
private enum Unit {
33+
KiB(1024L),
34+
MiB(1024L * 1024L),
35+
GiB(1024L * 1024L * 1024L);
36+
37+
private final long value;
38+
39+
Unit(long value) {
40+
this.value = value;
41+
}
42+
}
43+
44+
// We want to respect MAX_WIDTH and keep it concise,
45+
// so we prefer to show 0.99MiB than 1010.00KiB (length 10).
3246
public static String bytesToHuman(long bytes) {
3347
assert bytes >= 0;
34-
if (bytes < BYTES_TO_KB) {
35-
return plainBytes(bytes, "B");
36-
} else if (bytes < BYTES_TO_MB) {
37-
return toHuman(bytes / BYTES_TO_KB, "kB");
38-
} else if (bytes < BYTES_TO_GB) {
39-
return toHuman(bytes / BYTES_TO_MB, "MB");
48+
if (bytes < 1_000) {
49+
return bytes + "B";
50+
} else if (bytes < 1_000 * Unit.KiB.value) {
51+
return toHuman(bytes, Unit.KiB);
52+
} else if (bytes < 1_000 * Unit.MiB.value) {
53+
return toHuman(bytes, Unit.MiB);
4054
} else {
4155
return bytesToHumanGB(bytes);
4256
}
4357
}
4458

4559
public static String bytesToHumanGB(long bytes) {
46-
return toHuman(bytes / BYTES_TO_GB, "GB");
60+
return toHuman(bytes, Unit.GiB);
4761
}
4862

49-
private static String toHuman(double value, String unit) {
50-
return "%.2f%s".formatted(value, unit);
63+
private static String toHuman(long value, Unit unit) {
64+
String string = "%.2f%s".formatted((double) value / unit.value, unit);
65+
assert string.length() <= MAX_WIDTH || value >= 1000L * Unit.GiB.value;
66+
return string;
5167
}
5268

53-
private static String plainBytes(long value, String unit) {
54-
assert 0 <= value && value < BYTES_TO_KB;
55-
return "%d%s".formatted(value, unit);
56-
}
5769
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ public class ProgressReporter {
125125
public static final String DOCS_BASE_URL = "https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/BuildOutput.md";
126126
private static final double EXCESSIVE_GC_MIN_THRESHOLD_MILLIS = TimeUtils.secondsToMillis(15);
127127
private static final double EXCESSIVE_GC_RATIO = 0.5;
128+
// Use a leading space like in the rest of Native Image output
129+
private static final String BYTES_TO_HUMAN_FORMAT = " " + ByteFormattingUtil.RIGHT_ALIGNED_FORMAT;
128130

129131
private final NativeImageSystemIOWrappers builderIO;
130132

@@ -600,7 +602,7 @@ public void printCreationEnd(int imageFileSize, int heapObjectCount, long imageH
600602
Timer archiveTimer = getTimer(TimerCollection.Registry.ARCHIVE_LAYER);
601603
stagePrinter.end(imageTimer.getTotalTime() + writeTimer.getTotalTime() + archiveTimer.getTotalTime());
602604
creationStageEndCompleted = true;
603-
String format = "%9s (%5.2f%%) for ";
605+
String format = BYTES_TO_HUMAN_FORMAT + " (%5.2f%%) for ";
604606
l().a(format, ByteFormattingUtil.bytesToHuman(codeAreaSize), ProgressReporterUtils.toPercentage(codeAreaSize, imageFileSize))
605607
.doclink("code area", "#glossary-code-area").a(":%,10d compilation units", numCompilations).println();
606608
int numResources = 0;
@@ -634,7 +636,7 @@ public void printCreationEnd(int imageFileSize, int heapObjectCount, long imageH
634636
recordJsonMetric(ImageDetailKey.NUM_COMP_UNITS, numCompilations);
635637
l().a(format, ByteFormattingUtil.bytesToHuman(otherBytes), ProgressReporterUtils.toPercentage(otherBytes, imageFileSize))
636638
.doclink("other data", "#glossary-other-data").println();
637-
l().a("%9s in total image size", ByteFormattingUtil.bytesToHuman(imageFileSize));
639+
l().a(BYTES_TO_HUMAN_FORMAT + " in total image size", ByteFormattingUtil.bytesToHuman(imageFileSize));
638640
if (imageDiskFileSize >= 0) {
639641
l().a(", %s in total file size", ByteFormattingUtil.bytesToHuman(imageDiskFileSize));
640642
}
@@ -725,14 +727,15 @@ private void printBreakdowns() {
725727
int numHeapItems = heapBreakdown.getSortedBreakdownEntries().size();
726728
long totalCodeBytes = codeBreakdown.values().stream().mapToLong(Long::longValue).sum();
727729

728-
p.l().a(String.format("%9s for %s more packages", ByteFormattingUtil.bytesToHuman(totalCodeBytes - printedCodeBytes), numCodeItems - printedCodeItems))
730+
p.l().a(String.format(BYTES_TO_HUMAN_FORMAT + " for %s more packages", ByteFormattingUtil.bytesToHuman(totalCodeBytes - printedCodeBytes), numCodeItems - printedCodeItems))
729731
.jumpToMiddle()
730-
.a(String.format("%9s for %s more object types", ByteFormattingUtil.bytesToHuman(heapBreakdown.getTotalHeapSize() - printedHeapBytes), numHeapItems - printedHeapItems))
732+
.a(String.format(BYTES_TO_HUMAN_FORMAT + " for %s more object types", ByteFormattingUtil.bytesToHuman(heapBreakdown.getTotalHeapSize() - printedHeapBytes),
733+
numHeapItems - printedHeapItems))
731734
.flushln();
732735
}
733736

734737
private static String getBreakdownSizeString(long sizeInBytes) {
735-
return String.format("%9s ", ByteFormattingUtil.bytesToHuman(sizeInBytes));
738+
return String.format(BYTES_TO_HUMAN_FORMAT + " ", ByteFormattingUtil.bytesToHuman(sizeInBytes));
736739
}
737740

738741
private void printRecommendations() {

0 commit comments

Comments
 (0)