Skip to content

Commit 8bfc1ec

Browse files
committed
[GR-68165] Make DynamicObjectLibrary#setDynamicType transitions weak, same as putConstant transitions
PullRequest: graal/21674
2 parents 2cb5f70 + 2d7c849 commit 8bfc1ec

File tree

5 files changed

+97
-13
lines changed

5 files changed

+97
-13
lines changed

truffle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ This changelog summarizes major changes between Truffle versions relevant to lan
66
* GR-65048: Introduced `InternalResource.OS.UNSUPPORTED` and `InternalResource.CPUArchitecture.UNSUPPORTED` to represent unsupported platforms. Execution on unsupported platforms must be explicitly enabled using the system property `-Dpolyglot.engine.allowUnsupportedPlatform=true`. If this property is not set, calls to `OS.getCurrent()` or `CPUArchitecture.getCurrent()` will throw an `IllegalStateException` when running on an unsupported platform. `InternalResource` implementations should handle the unsupported platform and describe possible steps in the error message on how to proceed.
77
* GR-66839: Deprecate `Location#isFinal()` as it always returns false.
88
* GR-67702: Specialization DSL: For nodes annotated with `@GenerateInline`, inlining warnings emitted for `@Cached SomeNode helper` expressions are now suppressed if the helper node class is explicitly annotated with `@GenerateInline(false)`, or is not a DSL node. This avoids unnecessary warnings if inlining for a node was explicitly disabled, and makes it possible to remove `@Cached(inline = false)` in most cases.
9+
* GR-68165: `DynamicObjectLibrary#setDynamicType` transitions are now weak, like `DynamicObjectLibrary#putConstant` transitions.
910

1011
## Version 25.0
1112
* GR-31495 Added ability to specify language and instrument specific options using `Source.Builder.option(String, String)`. Languages may describe available source options by implementing `TruffleLanguage.getSourceOptionDescriptors()` and `TruffleInstrument.getSourceOptionDescriptors()` respectively.

truffle/src/com.oracle.truffle.api.object.test/src/com/oracle/truffle/object/basic/test/LeakCheckTest.java

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public void leakCheck() {
9393
}
9494

9595
/**
96-
* Make sure constant properties do not leak.
96+
* Make sure constant property transitions do not leak.
9797
*/
9898
@Test
9999
public void constantPropertyLeakCheck() {
@@ -127,7 +127,41 @@ public void constantPropertyLeakCheck() {
127127
}
128128

129129
/**
130-
* Make sure constant properties do not leak.
130+
* Make sure dynamic type transitions do not leak.
131+
*/
132+
@Test
133+
public void dynamicTypeLeakCheck() {
134+
Shape emptyShape = newEmptyShape();
135+
List<WeakReference<Shape>> weakShapeRefs = new ArrayList<>();
136+
List<Shape> strongShapeRefs = new ArrayList<>();
137+
138+
for (int i = 0; i < 100000; i++) {
139+
DynamicObject obj = newInstance(emptyShape);
140+
Leak value = new Leak();
141+
LIBRARY.setDynamicType(obj, value);
142+
143+
Shape shape = obj.getShape();
144+
value.shape = shape;
145+
weakShapeRefs.add(new WeakReference<>(shape));
146+
strongShapeRefs.add(shape);
147+
}
148+
149+
strongShapeRefs.clear();
150+
System.gc();
151+
152+
for (WeakReference<Shape> fullShapeRef : weakShapeRefs) {
153+
assertNull("Shape should have been garbage-collected", fullShapeRef.get());
154+
}
155+
156+
// trigger transition map cleanup
157+
DynamicObject obj = newInstance(emptyShape);
158+
LIBRARY.setDynamicType(obj, new Leak());
159+
160+
Reference.reachabilityFence(emptyShape);
161+
}
162+
163+
/**
164+
* Make sure constant property transitions do not leak.
131165
*/
132166
@Test
133167
public void constantPropertyLeakCheckSingleTransition() {
@@ -164,6 +198,47 @@ public void constantPropertyLeakCheckSingleTransition() {
164198
Reference.reachabilityFence(shapesToKeepAlive);
165199
}
166200

201+
/**
202+
* Make sure dynamic type transitions do not leak.
203+
*/
204+
@Test
205+
public void dynamicTypeLeakCheckSingleTransition() {
206+
List<Shape> shapesToKeepAlive = new ArrayList<>();
207+
List<WeakReference<Shape>> weakShapeRefs = new ArrayList<>();
208+
List<Shape> strongShapeRefs = new ArrayList<>();
209+
210+
for (int i = 0; i < 100000; i++) {
211+
Shape emptyShape = newEmptyShape();
212+
shapesToKeepAlive.add(emptyShape);
213+
DynamicObject obj = newInstance(emptyShape);
214+
215+
Leak leak1 = new Leak();
216+
LIBRARY.setDynamicType(obj, leak1);
217+
leak1.shape = obj.getShape();
218+
219+
Leak leak2 = new Leak();
220+
LIBRARY.setDynamicType(obj, leak2);
221+
leak2.shape = obj.getShape();
222+
223+
Leak leak3 = new Leak();
224+
LIBRARY.setDynamicType(obj, leak3);
225+
leak3.shape = obj.getShape();
226+
227+
Shape shape = obj.getShape();
228+
weakShapeRefs.add(new WeakReference<>(shape));
229+
strongShapeRefs.add(shape);
230+
}
231+
232+
strongShapeRefs.clear();
233+
System.gc();
234+
235+
for (WeakReference<Shape> fullShapeRef : weakShapeRefs) {
236+
assertNull("Shape should have been garbage-collected", fullShapeRef.get());
237+
}
238+
239+
Reference.reachabilityFence(shapesToKeepAlive);
240+
}
241+
167242
private static final class Leak {
168243
@SuppressWarnings("unused") Shape shape;
169244
@SuppressWarnings("unused") byte[] data = new byte[100];

truffle/src/com.oracle.truffle.api.object/src/com/oracle/truffle/api/object/DynamicObjectLibrary.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -317,9 +317,10 @@ public void putLong(DynamicObject object, Object key, long value) {
317317
* be used sparingly (with at most one constant value per property) since it could cause an
318318
* excessive amount of shapes to be created.
319319
* <p>
320-
* Note: the value will be strongly referenced from the shape and should be a value type or
321-
* light-weight object without any references to guest language objects in order to prevent
322-
* potential memory leaks.
320+
* Note: the value is strongly referenced from the shape property map. It should ideally be a
321+
* value type or light-weight object without any references to guest language objects in order
322+
* to prevent potential memory leaks from holding onto the Shape in inline caches. The Shape
323+
* transition itself is weak, so the previous shapes will not hold strongly on the value.
323324
*
324325
* <h3>Usage example:</h3>
325326
*
@@ -352,9 +353,11 @@ public void putLong(DynamicObject object, Object key, long value) {
352353
* Sets the object's dynamic type identifier. What this type represents is completely up to the
353354
* language. For example, it could be a guest-language class.
354355
*
355-
* The type object is strongly referenced from the shape. It is important that this be a
356-
* singleton or light-weight object without any references to guest language objects in order to
357-
* keep the memory footprint low and prevent potential memory leaks.
356+
* The type object is strongly referenced from the shape. It should ideally be a singleton or
357+
* light-weight object without any references to guest language objects in order to keep the
358+
* memory footprint low and prevent potential memory leaks from holding onto the Shape in inline
359+
* caches. The Shape transition itself is weak, so the previous shapes will not hold strongly on
360+
* the type object.
358361
*
359362
* Type objects are always compared by object identity, never {@code equals}.
360363
*

truffle/src/com.oracle.truffle.api.object/src/com/oracle/truffle/api/object/ShapeImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ private static Object newTransitionMap(Transition firstTransition, ShapeImpl fir
384384
}
385385

386386
private static ShapeImpl addToTransitionMap(Transition transition, ShapeImpl successor, TransitionMap<Transition, ShapeImpl> map) {
387-
if (transition.hasConstantLocation()) {
387+
if (transition.isWeak()) {
388388
return map.putWeakKeyIfAbsent(transition, successor);
389389
} else {
390390
return map.putIfAbsent(transition, successor);
@@ -416,7 +416,7 @@ private static boolean isTransitionMap(Object trans) {
416416
private static Object newSingleEntry(Transition transition, ShapeImpl successor) {
417417
transitionSingleEntriesCreated.inc();
418418
Object key = transition;
419-
if (transition.hasConstantLocation()) {
419+
if (transition.isWeak()) {
420420
key = new WeakKey<>(transition);
421421
}
422422
return new StrongKeyWeakValueEntry<>(key, successor);

truffle/src/com.oracle.truffle.api.object/src/com/oracle/truffle/api/object/Transition.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public boolean equals(Object obj) {
6969

7070
public abstract boolean isDirect();
7171

72-
protected boolean hasConstantLocation() {
72+
protected boolean isWeak() {
7373
return false;
7474
}
7575

@@ -127,7 +127,7 @@ public int getPropertyFlags() {
127127
}
128128

129129
@Override
130-
protected boolean hasConstantLocation() {
130+
protected boolean isWeak() {
131131
return getProperty().getLocation().isConstant();
132132
}
133133
}
@@ -244,6 +244,11 @@ public boolean isDirect() {
244244
return true;
245245
}
246246

247+
@Override
248+
protected boolean isWeak() {
249+
return true;
250+
}
251+
247252
@Override
248253
public String toString() {
249254
return String.format("objectType(%s)", getObjectType());
@@ -296,7 +301,7 @@ public String toString() {
296301
}
297302

298303
@Override
299-
protected boolean hasConstantLocation() {
304+
protected boolean isWeak() {
300305
return getPropertyBefore().getLocation().isConstant() || getPropertyAfter().getLocation().isConstant();
301306
}
302307
}

0 commit comments

Comments
 (0)