Skip to content

Commit 1d72666

Browse files
committed
Add ManagedObjectReferenceManager for CoreCLR
Add a new ManagedObjectReferenceManager that maintains GREF and WREF counters entirely in managed code using Interlocked operations, with zero P/Invoke overhead. Logging is disabled (LogGlobalReferenceMessages and LogLocalReferenceMessages return false). The gref_gc_threshold check for triggering full GC is preserved. AndroidObjectReferenceManager is kept unchanged for Mono. CoreCLR uses ManagedObjectReferenceManager, selected at runtime in AndroidRuntimeOptions. NativeAOT uses the existing Java.Interop.ManagedObjectReferenceManager with null log writers.
1 parent 6748bb1 commit 1d72666

File tree

2 files changed

+84
-2
lines changed

2 files changed

+84
-2
lines changed

src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/JreRuntime.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ static NativeAotRuntimeOptions CreateJreVM (NativeAotRuntimeOptions builder)
6262
#endif // NET
6363

6464
builder.ValueManager ??= ManagedValueManager.GetOrCreateInstance();
65-
builder.ObjectReferenceManager ??= new Android.Runtime.AndroidObjectReferenceManager ();
65+
builder.ObjectReferenceManager ??= new ManagedObjectReferenceManager (null, null);
6666

6767
if (builder.InvocationPointer != IntPtr.Zero || builder.EnvironmentPointer != IntPtr.Zero)
6868
return builder;

src/Mono.Android/Android.Runtime/AndroidRuntime.cs

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
using Java.Interop.Tools.TypeNameMappings;
1414
using System.Diagnostics.CodeAnalysis;
1515

16+
using RuntimeFeature = Microsoft.Android.Runtime.RuntimeFeature;
17+
1618
#if JAVA_INTEROP
1719
namespace Android.Runtime {
1820

@@ -94,7 +96,9 @@ public AndroidRuntimeOptions (IntPtr jnienv,
9496
EnvironmentPointer = jnienv;
9597
ClassLoader = new JniObjectReference (classLoader, JniObjectReferenceType.Global);
9698
InvocationPointer = vm;
97-
ObjectReferenceManager = new AndroidObjectReferenceManager ();
99+
ObjectReferenceManager = RuntimeFeature.IsCoreClrRuntime
100+
? new ManagedObjectReferenceManager ()
101+
: new AndroidObjectReferenceManager ();
98102
TypeManager = typeManager;
99103
ValueManager = valueManager;
100104
UseMarshalMemberBuilder = false;
@@ -244,6 +248,84 @@ public override void DeleteWeakGlobalReference (ref JniObjectReference value)
244248
}
245249
}
246250

251+
/// <summary>
252+
/// Managed object reference manager that maintains GREF and WREF counters entirely in
253+
/// managed code using Interlocked operations, avoiding all P/Invoke overhead.
254+
/// Logging uses managed TextWriter streams — no RuntimeNativeMethods calls at all.
255+
/// Used for CoreCLR; Mono uses <see cref="AndroidObjectReferenceManager"/> instead.
256+
/// </summary>
257+
internal class ManagedObjectReferenceManager : JniRuntime.JniObjectReferenceManager {
258+
259+
int _grefc;
260+
int _weak_grefc;
261+
262+
public override int GlobalReferenceCount => Volatile.Read (ref _grefc);
263+
public override int WeakGlobalReferenceCount => Volatile.Read (ref _weak_grefc);
264+
265+
public override bool LogGlobalReferenceMessages => false;
266+
public override bool LogLocalReferenceMessages => false;
267+
268+
public override void WriteLocalReferenceLine (string format, params object?[] args)
269+
{
270+
}
271+
272+
public override void WriteGlobalReferenceLine (string format, params object?[] args)
273+
{
274+
}
275+
276+
public override JniObjectReference CreateLocalReference (JniObjectReference value, ref int localReferenceCount)
277+
{
278+
return base.CreateLocalReference (value, ref localReferenceCount);
279+
}
280+
281+
public override void DeleteLocalReference (ref JniObjectReference value, ref int localReferenceCount)
282+
{
283+
base.DeleteLocalReference (ref value, ref localReferenceCount);
284+
}
285+
286+
public override void CreatedLocalReference (JniObjectReference value, ref int localReferenceCount)
287+
{
288+
base.CreatedLocalReference (value, ref localReferenceCount);
289+
}
290+
291+
public override IntPtr ReleaseLocalReference (ref JniObjectReference value, ref int localReferenceCount)
292+
{
293+
return base.ReleaseLocalReference (ref value, ref localReferenceCount);
294+
}
295+
296+
public override JniObjectReference CreateGlobalReference (JniObjectReference value)
297+
{
298+
var r = base.CreateGlobalReference (value);
299+
int gc = Interlocked.Increment (ref _grefc);
300+
301+
if (gc >= JNIEnvInit.gref_gc_threshold) {
302+
Logger.Log (LogLevel.Warn, "monodroid-gc", gc + " outstanding GREFs. Performing a full GC!");
303+
System.GC.Collect ();
304+
}
305+
306+
return r;
307+
}
308+
309+
public override void DeleteGlobalReference (ref JniObjectReference value)
310+
{
311+
Interlocked.Decrement (ref _grefc);
312+
base.DeleteGlobalReference (ref value);
313+
}
314+
315+
public override JniObjectReference CreateWeakGlobalReference (JniObjectReference value)
316+
{
317+
var r = base.CreateWeakGlobalReference (value);
318+
Interlocked.Increment (ref _weak_grefc);
319+
return r;
320+
}
321+
322+
public override void DeleteWeakGlobalReference (ref JniObjectReference value)
323+
{
324+
Interlocked.Decrement (ref _weak_grefc);
325+
base.DeleteWeakGlobalReference (ref value);
326+
}
327+
}
328+
247329
class AndroidTypeManager : JniRuntime.JniTypeManager {
248330
struct JniRemappingReplacementMethod
249331
{

0 commit comments

Comments
 (0)