diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/handles/ObjectHandlesImpl.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/handles/ObjectHandlesImpl.java
index 6c5d6417dd45..8269d6e98ef4 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/handles/ObjectHandlesImpl.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/handles/ObjectHandlesImpl.java
@@ -38,10 +38,7 @@
* This class implements {@link ObjectHandle word}-sized integer handles that refer to Java objects.
* {@link #create(Object) Creating}, {@link #get(ObjectHandle) dereferencing} and
* {@link #destroy(ObjectHandle) destroying} handles is thread-safe and the handles themselves are
- * valid across threads. This class also supports weak handles, with which the referenced object may
- * be garbage-collected, after which {@link #get(ObjectHandle)} returns {@code null}. Still, weak
- * handles must also be {@link #destroyWeak(ObjectHandle) explicitly destroyed} to reclaim their
- * handle value.
+ * valid across threads.
*
* The implementation uses a variable number of object arrays, in which each array element
* represents a handle. The array element's index determines the handle's integer value, and the
@@ -53,13 +50,6 @@
*/
public final class ObjectHandlesImpl implements ObjectHandles {
- /** Private subclass to distinguish from regular handles to {@link WeakReference} objects. */
- private static final class HandleWeakReference extends WeakReference {
- HandleWeakReference(T referent) {
- super(referent);
- }
- }
-
private static final int MAX_FIRST_BUCKET_CAPACITY = 1024;
static { // must be a power of 2 for the arithmetic below to work
assert Integer.lowestOneBit(MAX_FIRST_BUCKET_CAPACITY) == MAX_FIRST_BUCKET_CAPACITY;
@@ -210,17 +200,10 @@ public ObjectHandle create(Object obj) {
}
}
- public ObjectHandle createWeak(Object obj) {
- return create(new HandleWeakReference<>(obj));
- }
-
@SuppressWarnings("unchecked")
@Override
public T get(ObjectHandle handle) {
Object obj = doGet(handle);
- if (obj instanceof HandleWeakReference) {
- obj = ((HandleWeakReference) obj).get();
- }
return (T) obj;
}
@@ -240,10 +223,6 @@ private Object doGet(ObjectHandle handle) {
return Unsafe.getUnsafe().getReferenceVolatile(bucket, getObjectArrayByteOffset(indexInBucket));
}
- public boolean isWeak(ObjectHandle handle) {
- return (doGet(handle) instanceof HandleWeakReference);
- }
-
@Override
public void destroy(ObjectHandle handle) {
if (handle.equal(nullHandle)) {
@@ -261,10 +240,6 @@ public void destroy(ObjectHandle handle) {
Unsafe.getUnsafe().putReferenceRelease(bucket, getObjectArrayByteOffset(indexInBucket), null);
}
- public void destroyWeak(ObjectHandle handle) {
- destroy(handle);
- }
-
public long computeCurrentCount() {
long count = 0;
int bucketIndex = 0;
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIObjectHandles.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIObjectHandles.java
index 91b39f6ec6eb..13e8a954716d 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIObjectHandles.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIObjectHandles.java
@@ -220,6 +220,7 @@ public static void deleteLocalRef(JNIObjectHandle localRef) {
}
}
+ // in frames are local handles stored
public static int pushLocalFrame(int capacity) {
return getOrCreateLocals().pushFrame(capacity);
}
@@ -228,6 +229,7 @@ public static void popLocalFrame() {
getExistingLocals().popFrame();
}
+ // pops frames down to a specific starting point identified by the int frame
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static void popLocalFramesIncluding(int frame) {
getExistingLocals().popFramesIncluding(frame);
@@ -293,19 +295,37 @@ static long computeCurrentGlobalHandleCount() {
* for example by native code that is unaware of isolates.
*/
final class JNIGlobalHandles {
- static final SignedWord MIN_VALUE = Word.signed(Long.MIN_VALUE);
- static final SignedWord MAX_VALUE = JNIObjectHandles.nullHandle().subtract(1);
+ static final SignedWord MIN_VALUE = Word.signed(Long.MIN_VALUE); // -2^63
+ static final SignedWord MAX_VALUE = JNIObjectHandles.nullHandle().subtract(1); // -1
+
static {
assert JNIObjectHandles.nullHandle().equal(Word.zero());
}
+ // Handle=(MSB)+(Validation Tag)+(Handle Index)
private static final int HANDLE_BITS_COUNT = 31;
private static final SignedWord HANDLE_BITS_MASK = Word.signed((1L << HANDLE_BITS_COUNT) - 1);
private static final int VALIDATION_BITS_SHIFT = HANDLE_BITS_COUNT;
- private static final int VALIDATION_BITS_COUNT = 32;
+ private static final int VALIDATION_BITS_COUNT = 31;
private static final SignedWord VALIDATION_BITS_MASK = Word.signed((1L << VALIDATION_BITS_COUNT) - 1).shiftLeft(VALIDATION_BITS_SHIFT);
+ private static final SignedWord WEAK_HANDLE_FLAG = Word.signed(1L << 62);
private static final SignedWord MSB = Word.signed(1L << 63);
- private static final ObjectHandlesImpl globalHandles = new ObjectHandlesImpl(JNIObjectHandles.nullHandle().add(1), HANDLE_BITS_MASK, JNIObjectHandles.nullHandle());
+
+ // Define the mid-point to split the range in half
+ private static final SignedWord HANDLE_RANGE_SPLIT_POINT = Word.signed(1L << 30);
+
+ // Strong global handles will occupy the lower half of the global handles range
+ public static final SignedWord STRONG_GLOBAL_RANGE_MIN = JNIObjectHandles.nullHandle().add(1);;
+ public static final SignedWord STRONG_GLOBAL_RANGE_MAX = HANDLE_RANGE_SPLIT_POINT.subtract(1);
+
+ // Weak global handles will occupy the upper half of the global handles range
+ public static final SignedWord WEAK_GLOBAL_RANGE_MIN = HANDLE_RANGE_SPLIT_POINT;
+ public static final SignedWord WEAK_GLOBAL_RANGE_MAX = HANDLE_BITS_MASK;
+
+ private static final ObjectHandlesImpl strongGlobalHandles
+ = new ObjectHandlesImpl(STRONG_GLOBAL_RANGE_MIN, STRONG_GLOBAL_RANGE_MAX, JNIObjectHandles.nullHandle());
+ private static final ObjectHandlesImpl weakGlobalHandles
+ = new ObjectHandlesImpl(WEAK_GLOBAL_RANGE_MIN, WEAK_GLOBAL_RANGE_MAX, JNIObjectHandles.nullHandle());
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
static boolean isInRange(JNIObjectHandle handle) {
@@ -317,7 +337,20 @@ private static Word isolateHash() {
return Word.unsigned(isolateHash);
}
- private static JNIObjectHandle encode(ObjectHandle handle) {
+ /**
+ * Encodes a raw {@code ObjectHandle} into a strong {@code JNIObjectHandle}.
+ * * A strong handle guarantees the referenced object remains alive as long as
+ * the handle itself exists.
+ * * The handle is encoded by:
+ * 1. Asserting the handle fits within the available bit range.
+ * 2. Inserting validation bits (derived from the isolate hash) for security.
+ * 3. Setting the Most Significant Bit (MSB, bit 63) to mark it as an encoded handle.
+ * 4. The WEAK_HANDLE_FLAG bit (bit 62) remains 0.
+ *
+ * @param handle The raw, unencoded handle to the Java object.
+ * @return The resulting strong JNI object handle with embedded metadata.
+ */
+ private static JNIObjectHandle encodeStrong(ObjectHandle handle) {
SignedWord h = (Word) handle;
if (JNIObjectHandles.haveAssertions()) {
assert h.and(HANDLE_BITS_MASK).equal(h) : "unencoded handle must fit in range";
@@ -330,43 +363,74 @@ private static JNIObjectHandle encode(ObjectHandle handle) {
return (JNIObjectHandle) h;
}
+ /**
+ * Encodes a raw {@code ObjectHandle} into a weak {@code JNIObjectHandle}.
+ * * A weak handle allows the referenced object to be garbage collected even
+ * if the handle exists. The handle will be cleared when the object dies.
+ * * This method calls {@link #encodeStrong(ObjectHandle)} to perform all
+ * common encoding steps, and then explicitly sets the {@code WEAK_HANDLE_FLAG}
+ * bit (bit 62) to mark the handle as weak.
+ *
+ * @param handle The raw, unencoded handle to the Java object.
+ * @return The resulting weak JNI object handle with embedded metadata.
+ */
+ private static JNIObjectHandle encodeWeak(ObjectHandle handle) {
+ SignedWord h = (Word) encodeStrong(handle);
+ h = h.or(WEAK_HANDLE_FLAG);
+ assert isInRange((JNIObjectHandle) h);
+ return (JNIObjectHandle) h;
+ }
+
private static ObjectHandle decode(JNIObjectHandle handle) {
assert isInRange(handle);
assert ((Word) handle).and(VALIDATION_BITS_MASK).unsignedShiftRight(VALIDATION_BITS_SHIFT)
- .equal(isolateHash()) : "mismatching validation value -- passed a handle from a different isolate?";
+ .equal(isolateHash()) : "mismatching validation value -- passed a handle from a different isolate?";
return (ObjectHandle) HANDLE_BITS_MASK.and((Word) handle);
}
static T getObject(JNIObjectHandle handle) {
- return globalHandles.get(decode(handle));
+ SignedWord handleValue = (Word) handle;
+ if ((handleValue.toLong() & WEAK_HANDLE_FLAG.toLong()) == 0) {
+ return strongGlobalHandles.get(decode(handle));
+ }
+
+ if ((handleValue.toLong() & WEAK_HANDLE_FLAG.toLong()) == 1) {
+ return weakGlobalHandles.get(decode((handle)));
+ }
+
+ throw new IllegalArgumentException("Invalid handle");
}
static JNIObjectRefType getHandleType(JNIObjectHandle handle) {
- assert isInRange(handle);
- if (globalHandles.isWeak(decode(handle))) {
+ SignedWord handleValue = (Word) handle;
+ if ((handleValue.toLong() & WEAK_HANDLE_FLAG.toLong()) == 0) {
+ return JNIObjectRefType.Global;
+ }
+
+ if ((handleValue.toLong() & WEAK_HANDLE_FLAG.toLong()) == 1) {
return JNIObjectRefType.WeakGlobal;
}
- return JNIObjectRefType.Global;
+ return JNIObjectRefType.Invalid;
}
static JNIObjectHandle create(Object obj) {
- return encode(globalHandles.create(obj));
+ return encodeStrong(strongGlobalHandles.create(obj));
}
static void destroy(JNIObjectHandle handle) {
- globalHandles.destroy(decode(handle));
+ strongGlobalHandles.destroy(decode(handle));
}
static JNIObjectHandle createWeak(Object obj) {
- return encode(globalHandles.createWeak(obj));
+ return encodeWeak(weakGlobalHandles.create(obj));
}
static void destroyWeak(JNIObjectHandle weakRef) {
- globalHandles.destroyWeak(decode(weakRef));
+ weakGlobalHandles.destroy(decode(weakRef));
}
public static long computeCurrentCount() {
- return globalHandles.computeCurrentCount();
+ return strongGlobalHandles.computeCurrentCount() + weakGlobalHandles.computeCurrentCount();
}
}
@@ -375,7 +439,7 @@ public static long computeCurrentCount() {
* move and are not garbage-collected, so the handle just contains an object's offset in the image
* heap. This approach has the major benefit that handles are valid across isolates that are created
* from the same image, which helps with native code that is unaware of multiple isolates.
- *
+ *
* Although this type of handle doesn't need explicit management, we still distinguish between
* local, global and weak-global references by means of a bit pattern in order to comply with the
* JNI specification (in particular, for function {@code GetObjectRefType}).