44using System . Runtime . CompilerServices ;
55using System . Runtime . InteropServices ;
66using System . Runtime . InteropServices . Java ;
7+ using System . Threading ;
78using Android . Runtime ;
89using Java . Interop ;
910
@@ -33,18 +34,16 @@ unsafe class BridgeProcessing
3334 static JniObjectReference s_RuntimeInstance ;
3435 static JniMethodInfo ? s_Runtime_gc ;
3536
36- // Is NativeAOT mode (uses GCUserPeerable instead of IGCUserPeer )
37- static readonly bool s_isNativeAOT ;
37+ // Logger instance (null if logging is disabled )
38+ static readonly BridgeProcessingLogger ? s_logger ;
3839
3940 static BridgeProcessing ( )
4041 {
41- s_isNativeAOT = ! RuntimeFeature . IsMonoRuntime ;
42-
4342 // Initialize GCUserPeer class for creating temporary peers
4443 s_GCUserPeerClass = new JniType ( "mono/android/GCUserPeer" ) ;
4544 s_GCUserPeerCtor = s_GCUserPeerClass . GetConstructor ( "()V" ) ;
4645
47- if ( s_isNativeAOT ) {
46+ if ( ! RuntimeFeature . IsCoreClrRuntime ) {
4847 // For NativeAOT, we also use GCUserPeerable interface for optimized method calls
4948 s_GCUserPeerableClass = new JniType ( "net/dot/jni/GCUserPeerable" ) ;
5049 s_GCUserPeerable_jiAddManagedReference = s_GCUserPeerableClass . GetInstanceMethod ( "jiAddManagedReference" , "(Ljava/lang/Object;)V" ) ;
@@ -58,6 +57,11 @@ static BridgeProcessing ()
5857 var runtimeLocal = JniEnvironment . StaticMethods . CallStaticObjectMethod ( runtimeClass . PeerReference , getRuntimeMethod , null ) ;
5958 s_RuntimeInstance = runtimeLocal . NewGlobalRef ( ) ;
6059 JniObjectReference . Dispose ( ref runtimeLocal ) ;
60+
61+ // Initialize logger if logging is enabled
62+ if ( Logger . LogGC || Logger . LogGlobalRef ) {
63+ s_logger = new BridgeProcessingLogger ( ) ;
64+ }
6165 }
6266
6367 public BridgeProcessing ( MarkCrossReferencesArgs * args )
@@ -85,6 +89,7 @@ public void Process ()
8589 PrepareForJavaCollection ( ) ;
8690 TriggerJavaGC ( ) ;
8791 CleanupAfterJavaCollection ( ) ;
92+ s_logger ? . LogGcSummary ( crossRefs ) ;
8893 }
8994
9095 /// <summary>
@@ -206,8 +211,10 @@ bool AddReference (JniObjectReference from, JniObjectReference to)
206211 }
207212
208213 // Try the optimized path for GCUserPeerable (NativeAOT)
209- if ( s_isNativeAOT && TryCallGCUserPeerableAddManagedReference ( from , to ) ) {
210- return true ;
214+ if ( ! RuntimeFeature . IsCoreClrRuntime ) {
215+ if ( TryCallGCUserPeerableAddManagedReference ( from , to ) ) {
216+ return true ;
217+ }
211218 }
212219
213220 // Fall back to reflection-based approach
@@ -218,7 +225,7 @@ bool AddReference (JniObjectReference from, JniObjectReference to)
218225 try {
219226 addMethod = fromClass . GetInstanceMethod ( "monodroidAddReference" , "(Ljava/lang/Object;)V" ) ;
220227 } catch ( Java . Lang . NoSuchMethodError ) {
221- Logger . Log ( LogLevel . Error , "monodroid-gc" , "Failed to find monodroidAddReference method" ) ;
228+ s_logger ? . LogMissingAddReferencesMethod ( fromClass ) ;
222229 return false ;
223230 }
224231
@@ -266,12 +273,16 @@ void TakeWeakGlobalRef (HandleContext* context)
266273 Debug . Assert ( context ->ControlBlock ->HandleType == ( int ) JniObjectReferenceType . Global , "Expected global reference type for handle" ) ;
267274
268275 var handle = new JniObjectReference ( context ->ControlBlock ->Handle , JniObjectReferenceType . Global ) ;
276+ s_logger ? . LogTakeWeakGlobalRef ( handle ) ;
277+
269278 var weak = handle . NewWeakGlobalRef ( ) ;
279+ s_logger ? . LogWeakGrefNew ( handle , weak ) ;
270280
271281 context ->ControlBlock ->Handle = weak . Handle ;
272282 context ->ControlBlock ->HandleType = ( int ) JniObjectReferenceType . WeakGlobal ;
273283
274284 // Delete the old global ref
285+ s_logger ? . LogGrefDelete ( handle ) ;
275286 JniObjectReference . Dispose ( ref handle ) ;
276287 }
277288
@@ -300,12 +311,18 @@ void TakeGlobalRef (HandleContext* context)
300311
301312 var weak = new JniObjectReference ( context ->ControlBlock ->Handle , JniObjectReferenceType . WeakGlobal ) ;
302313 var handle = weak . NewGlobalRef ( ) ;
314+ s_logger ? . LogWeakToGref ( weak , handle ) ;
303315
304316 // The weak reference might have been collected
317+ if ( handle . Handle == IntPtr . Zero ) {
318+ s_logger ? . LogWeakRefCollected ( weak ) ;
319+ }
320+
305321 context ->ControlBlock ->Handle = handle . Handle ; // This may be null if collected
306322 context ->ControlBlock ->HandleType = ( int ) JniObjectReferenceType . Global ;
307323
308324 // Delete the old weak ref
325+ s_logger ? . LogWeakRefDelete ( weak ) ;
309326 JniObjectReference . Dispose ( ref weak ) ;
310327 }
311328
@@ -335,8 +352,10 @@ void ClearReferences (JniObjectReference handle)
335352 }
336353
337354 // Try the optimized path for GCUserPeerable (NativeAOT)
338- if ( s_isNativeAOT && TryCallGCUserPeerableClearManagedReferences ( handle ) ) {
339- return ;
355+ if ( ! RuntimeFeature . IsCoreClrRuntime ) {
356+ if ( TryCallGCUserPeerableClearManagedReferences ( handle ) ) {
357+ return ;
358+ }
340359 }
341360
342361 // Fall back to reflection-based approach
@@ -347,7 +366,7 @@ void ClearReferences (JniObjectReference handle)
347366 try {
348367 clearMethod = javaClass . GetInstanceMethod ( "monodroidClearReferences" , "()V" ) ;
349368 } catch ( Java . Lang . NoSuchMethodError ) {
350- Logger . Log ( LogLevel . Error , "monodroid-gc" , "Failed to find monodroidClearReferences method" ) ;
369+ s_logger ? . LogMissingClearReferencesMethod ( javaClass ) ;
351370 return ;
352371 }
353372
@@ -418,3 +437,116 @@ internal struct HandleContext
418437 public JniObjectReferenceControlBlock * ControlBlock ;
419438 }
420439}
440+
441+ /// <summary>
442+ /// Logger for GC bridge processing operations.
443+ /// Mirrors the logging from the original C++ bridge-processing.cc.
444+ /// </summary>
445+ unsafe class BridgeProcessingLogger
446+ {
447+ const string LogTag = "monodroid-gc" ;
448+
449+ public void LogMissingAddReferencesMethod ( JniType javaClass )
450+ {
451+ Logger . Log ( LogLevel . Error , LogTag , "Failed to find monodroidAddReference method" ) ;
452+ if ( Logger . LogGC ) {
453+ var className = GetClassName ( javaClass ) ;
454+ Logger . Log ( LogLevel . Error , LogTag , $ "Missing monodroidAddReference method for object of class { className } ") ;
455+ }
456+ }
457+
458+ public void LogMissingClearReferencesMethod ( JniType javaClass )
459+ {
460+ Logger . Log ( LogLevel . Error , LogTag , "Failed to find monodroidClearReferences method" ) ;
461+ if ( Logger . LogGC ) {
462+ var className = GetClassName ( javaClass ) ;
463+ Logger . Log ( LogLevel . Error , LogTag , $ "Missing monodroidClearReferences method for object of class { className } ") ;
464+ }
465+ }
466+
467+ public void LogWeakToGref ( JniObjectReference weak , JniObjectReference handle )
468+ {
469+ if ( ! Logger . LogGlobalRef ) {
470+ return ;
471+ }
472+ Logger . Log ( LogLevel . Info , LogTag , $ "take_global_ref wref=0x{ weak . Handle : x} -> handle=0x{ handle . Handle : x} ") ;
473+ }
474+
475+ public void LogWeakRefCollected ( JniObjectReference weak )
476+ {
477+ if ( ! Logger . LogGC ) {
478+ return ;
479+ }
480+ Logger . Log ( LogLevel . Info , LogTag , $ "handle 0x{ weak . Handle : x} /W; was collected by a Java GC") ;
481+ }
482+
483+ public void LogTakeWeakGlobalRef ( JniObjectReference handle )
484+ {
485+ if ( ! Logger . LogGlobalRef ) {
486+ return ;
487+ }
488+ Logger . Log ( LogLevel . Info , LogTag , $ "take_weak_global_ref handle=0x{ handle . Handle : x} ") ;
489+ }
490+
491+ public void LogWeakGrefNew ( JniObjectReference handle , JniObjectReference weak )
492+ {
493+ if ( ! Logger . LogGlobalRef ) {
494+ return ;
495+ }
496+ Logger . Log ( LogLevel . Info , LogTag , $ "weak_gref_new handle=0x{ handle . Handle : x} -> weak=0x{ weak . Handle : x} ") ;
497+ }
498+
499+ public void LogGrefDelete ( JniObjectReference handle )
500+ {
501+ if ( ! Logger . LogGlobalRef ) {
502+ return ;
503+ }
504+ Logger . Log ( LogLevel . Info , LogTag , $ "gref_delete handle=0x{ handle . Handle : x} at [[clr-gc:take_weak_global_ref]]") ;
505+ }
506+
507+ public void LogWeakRefDelete ( JniObjectReference weak )
508+ {
509+ if ( ! Logger . LogGlobalRef ) {
510+ return ;
511+ }
512+ Logger . Log ( LogLevel . Info , LogTag , $ "weak_ref_delete weak=0x{ weak . Handle : x} at [[clr-gc:take_global_ref]]") ;
513+ }
514+
515+ public void LogGcSummary ( MarkCrossReferencesArgs * crossRefs )
516+ {
517+ if ( ! Logger . LogGC ) {
518+ return ;
519+ }
520+
521+ nuint total = 0 ;
522+ nuint alive = 0 ;
523+
524+ for ( nuint i = 0 ; i < crossRefs ->ComponentCount ; i ++ ) {
525+ ref StronglyConnectedComponent scc = ref crossRefs ->Components [ i ] ;
526+
527+ for ( nuint j = 0 ; j < scc . Count ; j ++ ) {
528+ BridgeProcessing . HandleContext * context = ( BridgeProcessing . HandleContext * ) scc . Contexts [ j ] ;
529+ if ( context == null ) {
530+ continue ;
531+ }
532+
533+ total ++ ;
534+ if ( context ->ControlBlock != null && context ->ControlBlock ->Handle != IntPtr . Zero ) {
535+ alive ++ ;
536+ }
537+ }
538+ }
539+
540+ Logger . Log ( LogLevel . Info , LogTag , $ "GC cleanup summary: { total } objects tested - resurrecting { alive } .") ;
541+ }
542+
543+ static string GetClassName ( JniType javaClass )
544+ {
545+ try {
546+ // Get class name via Java reflection
547+ return javaClass . PeerReference . IsValid ? javaClass . Name : "(unknown)" ;
548+ } catch {
549+ return "(unknown)" ;
550+ }
551+ }
552+ }
0 commit comments