@@ -498,18 +498,30 @@ public static int pollReferenceQueue() {
498498 } else {
499499 assert nativeLookupGet (handleContext , reference .pointer ) != null : Long .toHexString (reference .pointer );
500500 LOGGER .finer (() -> PythonUtils .formatJString ("releasing native stub lookup for managed object with replacement %x => %s" , reference .pointer , reference ));
501- nativeLookupRemove (handleContext , reference .pointer );
502- if (reference .isAllocatedFromJava ()) {
503- LOGGER .finer (() -> PythonUtils .formatJString ("freeing managed object %s replacement" , reference ));
504- freeNativeStruct (reference );
501+ if (nativeLookupRemove (handleContext , reference .pointer ) != null ) {
502+ // The reference was still in our lookup table, it was not otherwise
503+ // freed and we can process it now.
504+ if (reference .isAllocatedFromJava ()) {
505+ LOGGER .finer (() -> PythonUtils .formatJString ("freeing managed object %s replacement" , reference ));
506+ freeNativeStruct (reference );
507+ } else {
508+ referencesToBeFreed .add (reference .pointer );
509+ }
505510 } else {
506- referencesToBeFreed .add (reference .pointer );
511+ // This handle was removed from the native lookup table before,
512+ // probably during an explicit collection. This can happen during
513+ // shutdown when tp_dealloc is called for some objects and that
514+ // causes upcalls and reference queue polling on references that
515+ // were already removed and had their memory freed
507516 }
508517 }
509518 } else if (entry instanceof NativeObjectReference reference ) {
510- LOGGER .finer (() -> PythonUtils .formatJString ("releasing native lookup for native object %x => %s" , reference .pointer , reference ));
511- nativeLookupRemove (handleContext , reference .pointer );
512- processNativeObjectReference (reference , referencesToBeFreed );
519+ if (nativeLookupRemove (handleContext , reference .pointer ) != null ) {
520+ // The reference was still in our lookup table, it was not otherwise
521+ // freed and we can process it now
522+ LOGGER .finer (() -> PythonUtils .formatJString ("releasing native lookup for native object %x => %s" , reference .pointer , reference ));
523+ processNativeObjectReference (reference , referencesToBeFreed );
524+ }
513525 } else if (entry instanceof NativeStorageReference reference ) {
514526 handleContext .nativeStorageReferences .remove (reference );
515527 processNativeStorageReference (reference );
0 commit comments