Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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.
* <p>
* 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
Expand All @@ -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<T> extends WeakReference<T> {
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;
Expand Down Expand Up @@ -210,17 +200,10 @@ public ObjectHandle create(Object obj) {
}
}

public ObjectHandle createWeak(Object obj) {
return create(new HandleWeakReference<>(obj));
}

@SuppressWarnings("unchecked")
@Override
public <T> T get(ObjectHandle handle) {
Object obj = doGet(handle);
if (obj instanceof HandleWeakReference) {
obj = ((HandleWeakReference<T>) obj).get();
}
return (T) obj;
}

Expand All @@ -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)) {
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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);
Expand Down Expand Up @@ -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) {
Expand All @@ -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";
Expand All @@ -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> 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();
}
}

Expand All @@ -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.
*
* <p>
* 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}).
Expand Down