@@ -843,94 +843,107 @@ static OopStorage* object_handles() {
843
843
jlong JVMCIRuntime::make_oop_handle (const Handle & obj) {
844
844
assert (!Universe::heap ()->is_gc_active (), " can't extend the root set during GC" );
845
845
assert (oopDesc::is_oop (obj ()), " not an oop" );
846
- oop* ptr = object_handles ()->allocate ();
847
- jlong res = 0 ;
848
- if (ptr != nullptr ) {
849
- assert (*ptr == nullptr , " invariant" );
850
- NativeAccess<>::oop_store (ptr, obj ());
851
- res = (jlong) ptr;
852
- } else {
853
- vm_exit_out_of_memory (sizeof (oop), OOM_MALLOC_ERROR,
854
- " Cannot create JVMCI oop handle" );
855
- }
846
+
847
+ oop* ptr = OopHandle (object_handles (), obj ()).ptr_raw ();
856
848
MutexLocker ml (_lock);
857
849
_oop_handles.append (ptr);
858
- return res ;
850
+ return (jlong) ptr ;
859
851
}
860
852
861
- bool JVMCIRuntime::probe_oop_handle (jlong handle, int index) {
862
- oop* key = (oop*) handle;
863
- if (key == _oop_handles.at (index )) {
864
- _last_found_oop_handle_index = index ;
865
- return true ;
853
+ int JVMCIRuntime::release_and_clear_oop_handles () {
854
+ guarantee (_num_attached_threads == cannot_be_attached, " only call during JVMCI runtime shutdown" );
855
+ int released = release_cleared_oop_handles ();
856
+ if (_oop_handles.length () != 0 ) {
857
+ for (int i = 0 ; i < _oop_handles.length (); i++) {
858
+ oop* oop_ptr = _oop_handles.at (i);
859
+ guarantee (oop_ptr != nullptr , " release_cleared_oop_handles left null entry in _oop_handles" );
860
+ guarantee (*oop_ptr != nullptr , " unexpected cleared handle" );
861
+ // Satisfy OopHandles::release precondition that all
862
+ // handles being released are null.
863
+ NativeAccess<>::oop_store (oop_ptr, (oop) NULL );
864
+ }
865
+
866
+ // Do the bulk release
867
+ object_handles ()->release (_oop_handles.adr_at (0 ), _oop_handles.length ());
868
+ released += _oop_handles.length ();
866
869
}
867
- return false ;
870
+ _oop_handles.clear ();
871
+ return released;
868
872
}
869
873
870
- int JVMCIRuntime::find_oop_handle (jlong handle) {
871
- int len = _oop_handles.length ();
872
- int next = _last_found_oop_handle_index + 1 ;
873
- int prev = MAX2 (_last_found_oop_handle_index, 0 ) - 1 ;
874
+ static bool is_referent_non_null (oop* handle) {
875
+ return handle != nullptr && *handle != nullptr ;
876
+ }
874
877
875
- // Search "outwards" from the index of the last found
876
- // entry. Experimentation shows that this significantly
877
- // reduces the amount of searching performed.
878
- do {
879
- if (next < len) {
880
- if (probe_oop_handle (handle, next)) {
881
- return next;
882
- }
883
- next++;
884
- }
885
- if (prev >= 0 ) {
886
- if (probe_oop_handle (handle, prev)) {
887
- return prev;
888
- }
889
- prev--;
890
- }
891
- } while (next - (prev + 1 ) < len);
892
- return -1 ;
878
+ // Swaps the elements in `array` at index `a` and index `b`
879
+ static void swap (GrowableArray<oop*>* array, int a, int b) {
880
+ oop* tmp = array->at (a);
881
+ array->at_put (a, array->at (b));
882
+ array->at_put (b, tmp);
893
883
}
894
884
895
- int JVMCIRuntime::release_and_clear_globals () {
896
- int released = 0 ;
885
+ int JVMCIRuntime::release_cleared_oop_handles () {
886
+ // Despite this lock, it's possible for another thread
887
+ // to clear a handle's referent concurrently (e.g., a thread
888
+ // executing IndirectHotSpotObjectConstantImpl.clear()).
889
+ // This is benign - it means there can still be cleared
890
+ // handles in _oop_handles when this method returns.
891
+ MutexLocker ml (_lock);
892
+
893
+ int next = 0 ;
897
894
if (_oop_handles.length () != 0 ) {
898
- // Squash non-null JNI handles to front of _oop_handles for
899
- // the bulk release operation
895
+ // Key for _oop_handles contents in example below:
896
+ // H: handle with non-null referent
897
+ // h: handle with clear (i.e., null) referent
898
+ // -: null entry
899
+
900
+ // Shuffle all handles with non-null referents to the front of the list
901
+ // Example: Before: 0HHh-Hh-
902
+ // After: HHHh--h-
900
903
for (int i = 0 ; i < _oop_handles.length (); i++) {
901
- oop* oop_ptr = _oop_handles.at (i);
902
- if (oop_ptr != nullptr ) {
903
- // Satisfy OopHandles::release precondition that all
904
- // handles being released are null.
905
- NativeAccess<>::oop_store (oop_ptr, (oop) NULL );
904
+ oop* handle = _oop_handles.at (i);
905
+ if (is_referent_non_null (handle)) {
906
+ if (i != next && !is_referent_non_null (_oop_handles.at (next))) {
907
+ // Swap elements at index `next` and `i`
908
+ swap (&_oop_handles, next, i);
909
+ }
910
+ next++;
911
+ }
912
+ }
906
913
907
- _oop_handles.at_put (released++, oop_ptr);
914
+ // `next` is now the index of the first null handle or handle with a null referent
915
+ int num_alive = next;
916
+
917
+ // Shuffle all null handles to the end of the list
918
+ // Example: Before: HHHh--h-
919
+ // After: HHHhh---
920
+ // num_alive: 3
921
+ for (int i = next; i < _oop_handles.length (); i++) {
922
+ oop* handle = _oop_handles.at (i);
923
+ if (handle != nullptr ) {
924
+ if (i != next && _oop_handles.at (next) == nullptr ) {
925
+ // Swap elements at index `next` and `i`
926
+ swap (&_oop_handles, next, i);
927
+ }
928
+ next++;
908
929
}
909
930
}
910
- // Do the bulk release
911
- object_handles ()->release (_oop_handles.adr_at (0 ), released);
912
- }
913
- _oop_handles.clear ();
914
- _last_found_oop_handle_index = -1 ;
915
- return released;
916
- }
931
+ int to_release = next - num_alive;
917
932
918
- void JVMCIRuntime::destroy_oop_handle (jlong handle) {
919
- // Assert before nulling out, for better debugging.
920
- assert (is_oop_handle (handle), " precondition" );
921
- oop* oop_ptr = (oop*) handle;
922
- NativeAccess<>::oop_store (oop_ptr, (oop) nullptr );
923
- object_handles ()->release (oop_ptr);
933
+ // `next` is now the index of the first null handle
934
+ // Example: to_release: 2
924
935
925
- MutexLocker ml (_lock);
926
- int index = find_oop_handle (handle);
927
- guarantee (index != -1 , " global not allocated in JVMCI runtime %d: " INTPTR_FORMAT, id (), handle);
928
- _oop_handles.at_put (index , nullptr );
929
- }
936
+ // Bulk release the handles with a null referent
937
+ object_handles ()->release (_oop_handles.adr_at (num_alive), to_release);
930
938
931
- bool JVMCIRuntime::is_oop_handle (jlong handle) {
932
- const oop* ptr = (oop*) handle;
933
- return object_handles ()->allocation_status (ptr) == OopStorage::ALLOCATED_ENTRY;
939
+ // Truncate oop handles to only those with a non-null referent
940
+ JVMCI_event_1 (" compacted oop handles in JVMCI runtime %d from %d to %d" , _id, _oop_handles.length (), num_alive);
941
+ _oop_handles.trunc_to (num_alive);
942
+ // Example: HHH
943
+
944
+ return to_release;
945
+ }
946
+ return 0 ;
934
947
}
935
948
936
949
jmetadata JVMCIRuntime::allocate_handle (const methodHandle& handle) {
@@ -988,8 +1001,7 @@ JVMCIRuntime::JVMCIRuntime(JVMCIRuntime* next, int id, bool for_compile_broker)
988
1001
_metadata_handles(new MetadataHandles()),
989
1002
_oop_handles(100 , mtJVMCI),
990
1003
_num_attached_threads(0 ),
991
- _for_compile_broker(for_compile_broker),
992
- _last_found_oop_handle_index(-1 )
1004
+ _for_compile_broker(for_compile_broker)
993
1005
{
994
1006
if (id == -1 ) {
995
1007
_lock = JVMCIRuntime_lock;
@@ -1169,7 +1181,7 @@ bool JVMCIRuntime::detach_thread(JavaThread* thread, const char* reason, bool ca
1169
1181
// that could be using them. Handles for the Java JVMCI runtime
1170
1182
// are never released as we cannot guarantee all compiler threads
1171
1183
// using it have been stopped.
1172
- int released = release_and_clear_globals ();
1184
+ int released = release_and_clear_oop_handles ();
1173
1185
JVMCI_event_1 (" releasing handles for JVMCI runtime %d: oop handles=%d, metadata handles={total=%d, live=%d, blocks=%d}" ,
1174
1186
_id,
1175
1187
released,
0 commit comments