Skip to content

Commit e03b661

Browse files
committed
[GR-64192] Native Image Builder Memory Footprint Optimizations
PullRequest: graal/20565
2 parents 3a906b1 + 202a743 commit e03b661

File tree

9 files changed

+241
-53
lines changed

9 files changed

+241
-53
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package org.graalvm.collections.test;
42+
43+
import java.util.NoSuchElementException;
44+
45+
import org.graalvm.collections.EconomicSet;
46+
import org.junit.Assert;
47+
import org.junit.Test;
48+
49+
public class EmptyEconomicSetTest {
50+
51+
@Test
52+
public void testIsEmpty() {
53+
Assert.assertTrue(EconomicSet.emptySet().isEmpty());
54+
}
55+
56+
@Test
57+
public void testSizeZero() {
58+
Assert.assertEquals(0, EconomicSet.emptySet().size());
59+
}
60+
61+
@Test(expected = IllegalArgumentException.class)
62+
public void testAdd() {
63+
EconomicSet.emptySet().add(1);
64+
}
65+
66+
@Test(expected = IllegalArgumentException.class)
67+
public void testRemove() {
68+
EconomicSet.emptySet().remove(1);
69+
}
70+
71+
@Test(expected = IllegalArgumentException.class)
72+
public void testClear() {
73+
EconomicSet.emptySet().clear();
74+
}
75+
76+
@Test
77+
public void testContains() {
78+
Assert.assertFalse(EconomicSet.emptySet().contains(1));
79+
}
80+
81+
@Test
82+
public void testIteratorAlwaysEmpty() {
83+
Assert.assertFalse(EconomicSet.emptySet().iterator().hasNext());
84+
}
85+
86+
@Test(expected = NoSuchElementException.class)
87+
public void testIteratorThrowsException() {
88+
EconomicSet.emptySet().iterator().next();
89+
}
90+
}

sdk/src/org.graalvm.collections/snapshot.sigtest

+1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ meth public static <%0 extends java.lang.Object> org.graalvm.collections.Economi
136136
meth public static <%0 extends java.lang.Object> org.graalvm.collections.EconomicSet<{%%0}> create(org.graalvm.collections.Equivalence,int)
137137
meth public static <%0 extends java.lang.Object> org.graalvm.collections.EconomicSet<{%%0}> create(org.graalvm.collections.Equivalence,org.graalvm.collections.UnmodifiableEconomicSet<{%%0}>)
138138
meth public static <%0 extends java.lang.Object> org.graalvm.collections.EconomicSet<{%%0}> create(org.graalvm.collections.UnmodifiableEconomicSet<{%%0}>)
139+
meth public static org.graalvm.collections.EconomicSet emptySet()
139140
meth public void addAll(java.lang.Iterable<{org.graalvm.collections.EconomicSet%0}>)
140141
meth public void addAll(java.util.Iterator<{org.graalvm.collections.EconomicSet%0}>)
141142
meth public void addAll(org.graalvm.collections.EconomicSet<{org.graalvm.collections.EconomicSet%0}>)

sdk/src/org.graalvm.collections/src/org/graalvm/collections/EconomicSet.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -206,4 +206,14 @@ static <E> EconomicSet<E> create(Equivalence strategy, int initialCapacity) {
206206
static <E> EconomicSet<E> create(Equivalence strategy, UnmodifiableEconomicSet<E> c) {
207207
return EconomicMapImpl.create(strategy, c, true);
208208
}
209+
210+
/**
211+
* Return an empty, unmodifiable {@link EconomicSet}.
212+
*
213+
* @since 25.0
214+
*/
215+
@SuppressWarnings("unchecked")
216+
static <E> EconomicSet<E> emptySet() {
217+
return (EconomicSet<E>) EmptySet.EMPTY_SET;
218+
}
209219
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package org.graalvm.collections;
42+
43+
import java.util.Iterator;
44+
45+
/**
46+
* Singleton instance for empty set. Reuses empty iterator from {@link EmptyMap}.
47+
*/
48+
class EmptySet {
49+
static final EconomicSet<Object> EMPTY_SET = new EconomicSet<>() {
50+
@Override
51+
public boolean add(Object element) {
52+
throw new IllegalArgumentException("Cannot modify the always-empty set");
53+
}
54+
55+
@Override
56+
public void remove(Object element) {
57+
throw new IllegalArgumentException("Cannot modify the always-empty set");
58+
}
59+
60+
@Override
61+
public void clear() {
62+
throw new IllegalArgumentException("Cannot modify the always-empty set");
63+
}
64+
65+
@Override
66+
public boolean contains(Object element) {
67+
return false;
68+
}
69+
70+
@Override
71+
public int size() {
72+
return 0;
73+
}
74+
75+
@Override
76+
public boolean isEmpty() {
77+
return true;
78+
}
79+
80+
@Override
81+
public Iterator<Object> iterator() {
82+
return EmptyMap.EMPTY_ITERATOR;
83+
}
84+
};
85+
}

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/PointsToAnalysis.java

+9-6
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import com.oracle.graal.pointsto.api.HostVM;
4646
import com.oracle.graal.pointsto.api.PointstoOptions;
4747
import com.oracle.graal.pointsto.constraints.UnsupportedFeatures;
48+
import com.oracle.graal.pointsto.flow.AlwaysEnabledPredicateFlow;
4849
import com.oracle.graal.pointsto.flow.AnyPrimitiveSourceTypeFlow;
4950
import com.oracle.graal.pointsto.flow.FieldTypeFlow;
5051
import com.oracle.graal.pointsto.flow.FormalParamTypeFlow;
@@ -103,9 +104,9 @@ public abstract class PointsToAnalysis extends AbstractAnalysisEngine {
103104
*/
104105
private final boolean trackPrimitiveValues;
105106
private final AnalysisType longType;
106-
private final AnalysisType voidType;
107107
private final boolean usePredicates;
108108
private AnyPrimitiveSourceTypeFlow anyPrimitiveSourceTypeFlow;
109+
private AlwaysEnabledPredicateFlow alwaysEnabledPredicateFlow;
109110

110111
protected final boolean trackTypeFlowInputs;
111112
protected final boolean reportAnalysisStatistics;
@@ -127,12 +128,13 @@ public PointsToAnalysis(OptionValues options, AnalysisUniverse universe, HostVM
127128

128129
this.objectType = metaAccess.lookupJavaType(Object.class);
129130
this.longType = metaAccess.lookupJavaType(long.class);
130-
this.voidType = metaAccess.lookupJavaType(void.class);
131131

132132
this.trackPrimitiveValues = PointstoOptions.TrackPrimitiveValues.getValue(options);
133133
this.usePredicates = PointstoOptions.UsePredicates.getValue(options);
134134
this.anyPrimitiveSourceTypeFlow = new AnyPrimitiveSourceTypeFlow(null, longType);
135135
this.anyPrimitiveSourceTypeFlow.enableFlow(null);
136+
this.alwaysEnabledPredicateFlow = new AlwaysEnabledPredicateFlow();
137+
136138
/*
137139
* Make sure the all-instantiated type flow is created early. We do not have any
138140
* instantiated types yet, so the state is empty at first.
@@ -292,6 +294,7 @@ public boolean trackConcreteAnalysisObjects(@SuppressWarnings("unused") Analysis
292294
public void cleanupAfterAnalysis() {
293295
super.cleanupAfterAnalysis();
294296
anyPrimitiveSourceTypeFlow = null;
297+
alwaysEnabledPredicateFlow = null;
295298
unsafeLoads = null;
296299
unsafeStores = null;
297300

@@ -310,10 +313,6 @@ public AnalysisType getLongType() {
310313
return longType;
311314
}
312315

313-
public AnalysisType getVoidType() {
314-
return voidType;
315-
}
316-
317316
public AnalysisType getObjectArrayType() {
318317
return metaAccess.lookupJavaType(Object[].class);
319318
}
@@ -331,6 +330,10 @@ public AnyPrimitiveSourceTypeFlow getAnyPrimitiveSourceTypeFlow() {
331330
return anyPrimitiveSourceTypeFlow;
332331
}
333332

333+
public AlwaysEnabledPredicateFlow getAlwaysEnabledPredicateFlow() {
334+
return alwaysEnabledPredicateFlow;
335+
}
336+
334337
@Override
335338
public Iterable<AnalysisType> getAllSynchronizedTypes() {
336339
return getAllInstantiatedTypes();

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ public MethodTypeFlowBuilder(PointsToAnalysis bb, PointsToAnalysisMethod method,
208208
this.graphKind = graphKind;
209209
if (bb.trackPrimitiveValues()) {
210210
this.alwaysEnabled = bb.usePredicates()
211-
? TypeFlowBuilder.create(bb, method, null, PointsToAnalysis.syntheticSourcePosition(method), AlwaysEnabledPredicateFlow.class, AlwaysEnabledPredicateFlow::new)
211+
? TypeFlowBuilder.create(bb, method, null, PointsToAnalysis.syntheticSourcePosition(method), AlwaysEnabledPredicateFlow.class, bb::getAlwaysEnabledPredicateFlow)
212212
: null;
213213
this.anyPrimitiveSourceTypeFlowBuilder = TypeFlowBuilder.create(bb, method, alwaysEnabled, null, AnyPrimitiveSourceTypeFlow.class, bb::getAnyPrimitiveSourceTypeFlow);
214214
} else {

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/TypeFlow.java

+13-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -296,14 +296,14 @@ public void addPredicated(PointsToAnalysis bb, TypeFlow<?> predicatedFlow) {
296296
}
297297
}
298298

299-
private void removePredicated(TypeFlow<?> predicatedFlow) {
300-
ConcurrentLightHashSet.removeElement(this, PREDICATED_FLOWS_UPDATER, predicatedFlow);
301-
}
302-
303299
public Collection<TypeFlow<?>> getPredicatedFlows() {
304300
return ConcurrentLightHashSet.getElements(this, PREDICATED_FLOWS_UPDATER);
305301
}
306302

303+
public void clearPredicatedFlows() {
304+
ConcurrentLightHashSet.clear(this, PREDICATED_FLOWS_UPDATER);
305+
}
306+
307307
public boolean predicateAlreadyTriggered() {
308308
return AtomicUtils.isSet(this, PREDICATE_TRIGGERED_UPDATER);
309309
}
@@ -945,31 +945,34 @@ protected void onSaturated() {
945945
private void notifySaturated(PointsToAnalysis bb) {
946946
for (TypeFlow<?> use : getUses()) {
947947
notifyUseOfSaturation(bb, use);
948-
removeUse(use);
949948
}
949+
clearUses();
950950
for (TypeFlow<?> observer : getObservers()) {
951951
notifyObserverOfSaturation(bb, observer);
952-
removeObserver(observer);
953952
}
953+
clearObservers();
954954
}
955955

956956
/** This flow will swap itself out at all uses and observers. */
957957
protected void swapOut(PointsToAnalysis bb, TypeFlow<?> newFlow) {
958958
assert isSaturated() : "This operation should only be called on saturated flows:" + this;
959959
for (TypeFlow<?> use : getUses()) {
960-
swapAtUse(bb, newFlow, use);
960+
newFlow.addUse(bb, use);
961961
}
962+
clearUses();
962963
for (TypeFlow<?> observer : getObservers()) {
963-
swapAtObserver(bb, newFlow, observer);
964+
observer.replacedObservedWith(bb, newFlow);
964965
}
966+
clearObservers();
965967
/*
966968
* Before performing the swap, make sure addPredicated will immediately enable any newly
967969
* added predicated flows.
968970
*/
969971
AtomicUtils.atomicMark(this, PREDICATE_TRIGGERED_UPDATER);
970972
for (TypeFlow<?> predicatedFlow : getPredicatedFlows()) {
971-
swapAtPredicated(bb, newFlow, predicatedFlow);
973+
newFlow.addPredicated(bb, predicatedFlow);
972974
}
975+
clearPredicatedFlows();
973976
}
974977

975978
protected void swapAtUse(PointsToAnalysis bb, TypeFlow<?> newFlow, TypeFlow<?> use) {
@@ -983,11 +986,6 @@ protected void swapAtObserver(PointsToAnalysis bb, TypeFlow<?> newFlow, TypeFlow
983986
observer.replacedObservedWith(bb, newFlow);
984987
}
985988

986-
private void swapAtPredicated(PointsToAnalysis bb, TypeFlow<?> newFlow, TypeFlow<?> predicatedFlow) {
987-
removePredicated(predicatedFlow);
988-
newFlow.addPredicated(bb, predicatedFlow);
989-
}
990-
991989
/**
992990
* Notifies this flow that its input has saturated, but only runs the
993991
* {@link TypeFlow#onInputSaturated}} if this flow is enabled. Otherwise, the execution of

0 commit comments

Comments
 (0)