Skip to content

Commit 399676d

Browse files
committed
Replace AndroidObjectReferenceManager with ManagedObjectReferenceManager
Maintain GREF and WREF counters in managed code using Interlocked instead of P/Invoking into native _monodroid_gref_get/_monodroid_weak_gref_get. Guard all logging P/Invokes behind Logger.LogGlobalRef/Logger.LogLocalRef checks so no managed-to-native transition occurs when logging is disabled.
1 parent eeabf88 commit 399676d

File tree

2 files changed

+81
-63
lines changed

2 files changed

+81
-63
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 Android.Runtime.ManagedObjectReferenceManager ();
6666

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

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

Lines changed: 80 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -94,21 +94,41 @@ public AndroidRuntimeOptions (IntPtr jnienv,
9494
EnvironmentPointer = jnienv;
9595
ClassLoader = new JniObjectReference (classLoader, JniObjectReferenceType.Global);
9696
InvocationPointer = vm;
97-
ObjectReferenceManager = new AndroidObjectReferenceManager ();
97+
ObjectReferenceManager = new ManagedObjectReferenceManager ();
9898
TypeManager = typeManager;
9999
ValueManager = valueManager;
100100
UseMarshalMemberBuilder = false;
101101
JniAddNativeMethodRegistrationAttributePresent = jniAddNativeMethodRegistrationAttributePresent;
102102
}
103103
}
104104

105-
internal class AndroidObjectReferenceManager : JniRuntime.JniObjectReferenceManager {
106-
public override int GlobalReferenceCount {
107-
get {return RuntimeNativeMethods._monodroid_gref_get ();}
105+
/// <summary>
106+
/// Managed object reference manager that maintains GREF, LREF, and WREF counters
107+
/// entirely in managed code, avoiding P/Invoke overhead. Logging P/Invokes are only
108+
/// called when logging is actually enabled.
109+
/// </summary>
110+
internal class ManagedObjectReferenceManager : JniRuntime.JniObjectReferenceManager {
111+
112+
int _grefc;
113+
int _weak_grefc;
114+
115+
public override int GlobalReferenceCount => Volatile.Read (ref _grefc);
116+
117+
public override int WeakGlobalReferenceCount => Volatile.Read (ref _weak_grefc);
118+
119+
public override bool LogGlobalReferenceMessages => Logger.LogGlobalRef;
120+
public override bool LogLocalReferenceMessages => Logger.LogLocalRef;
121+
122+
public override void WriteLocalReferenceLine (string format, params object?[] args)
123+
{
124+
RuntimeNativeMethods._monodroid_gref_log ("[LREF] " + string.Format (CultureInfo.InvariantCulture, format, args));
125+
RuntimeNativeMethods._monodroid_gref_log ("\n");
108126
}
109127

110-
public override int WeakGlobalReferenceCount {
111-
get {return RuntimeNativeMethods._monodroid_weak_gref_get ();}
128+
public override void WriteGlobalReferenceLine (string format, params object?[] args)
129+
{
130+
RuntimeNativeMethods._monodroid_gref_log (string.Format (CultureInfo.InvariantCulture, format, args));
131+
RuntimeNativeMethods._monodroid_gref_log ("\n");
112132
}
113133

114134
public override JniObjectReference CreateLocalReference (JniObjectReference value, ref int localReferenceCount)
@@ -117,7 +137,7 @@ public override JniObjectReference CreateLocalReference (JniObjectReference valu
117137

118138
if (Logger.LogLocalRef) {
119139
var tname = Thread.CurrentThread.Name;
120-
var tid = Thread.CurrentThread.ManagedThreadId;;
140+
var tid = Thread.CurrentThread.ManagedThreadId;
121141
var from = new StringBuilder (new StackTrace (true).ToString ());
122142
RuntimeNativeMethods._monodroid_lref_log_new (localReferenceCount, r.Handle, (byte) 'L', tname, tid, from, 1);
123143
}
@@ -129,7 +149,7 @@ public override void DeleteLocalReference (ref JniObjectReference value, ref int
129149
{
130150
if (Logger.LogLocalRef) {
131151
var tname = Thread.CurrentThread.Name;
132-
var tid = Thread.CurrentThread.ManagedThreadId;;
152+
var tid = Thread.CurrentThread.ManagedThreadId;
133153
var from = new StringBuilder (new StackTrace (true).ToString ());
134154
RuntimeNativeMethods._monodroid_lref_log_delete (localReferenceCount-1, value.Handle, (byte) 'L', tname, tid, from, 1);
135155
}
@@ -141,7 +161,7 @@ public override void CreatedLocalReference (JniObjectReference value, ref int lo
141161
base.CreatedLocalReference (value, ref localReferenceCount);
142162
if (Logger.LogLocalRef) {
143163
var tname = Thread.CurrentThread.Name;
144-
var tid = Thread.CurrentThread.ManagedThreadId;;
164+
var tid = Thread.CurrentThread.ManagedThreadId;
145165
var from = new StringBuilder (new StackTrace (true).ToString ());
146166
RuntimeNativeMethods._monodroid_lref_log_new (localReferenceCount, value.Handle, (byte) 'L', tname, tid, from, 1);
147167
}
@@ -152,96 +172,94 @@ public override IntPtr ReleaseLocalReference (ref JniObjectReference value, ref
152172
var r = base.ReleaseLocalReference (ref value, ref localReferenceCount);
153173
if (Logger.LogLocalRef) {
154174
var tname = Thread.CurrentThread.Name;
155-
var tid = Thread.CurrentThread.ManagedThreadId;;
175+
var tid = Thread.CurrentThread.ManagedThreadId;
156176
var from = new StringBuilder (new StackTrace (true).ToString ());
157177
RuntimeNativeMethods._monodroid_lref_log_delete (localReferenceCount-1, value.Handle, (byte) 'L', tname, tid, from, 1);
158178
}
159179
return r;
160180
}
161181

162-
public override bool LogGlobalReferenceMessages => Logger.LogGlobalRef;
163-
public override bool LogLocalReferenceMessages => Logger.LogLocalRef;
164-
165-
public override void WriteLocalReferenceLine (string format, params object?[] args)
166-
{
167-
RuntimeNativeMethods._monodroid_gref_log ("[LREF] " + string.Format (CultureInfo.InvariantCulture, format, args));
168-
RuntimeNativeMethods._monodroid_gref_log ("\n");
169-
}
170-
171-
public override void WriteGlobalReferenceLine (string format, params object?[] args)
172-
{
173-
RuntimeNativeMethods._monodroid_gref_log (string.Format (CultureInfo.InvariantCulture, format, args));
174-
RuntimeNativeMethods._monodroid_gref_log ("\n");
175-
}
176-
177182
public override JniObjectReference CreateGlobalReference (JniObjectReference value)
178183
{
179-
var r = base.CreateGlobalReference (value);
184+
var r = base.CreateGlobalReference (value);
185+
int gc = Interlocked.Increment (ref _grefc);
180186

181-
var log = Logger.LogGlobalRef;
182-
var ctype = log ? GetObjectRefType (value.Type) : (byte) '*';
183-
var ntype = log ? GetObjectRefType (r.Type) : (byte) '*';
184-
var tname = log ? Thread.CurrentThread.Name : null;
185-
var tid = log ? Thread.CurrentThread.ManagedThreadId : 0;
186-
var from = log ? new StringBuilder (new StackTrace (true).ToString ()) : null;
187-
int gc = RuntimeNativeMethods._monodroid_gref_log_new (value.Handle, ctype, r.Handle, ntype, tname, tid, from, 1);
188187
if (gc >= JNIEnvInit.gref_gc_threshold) {
189188
Logger.Log (LogLevel.Warn, "monodroid-gc", gc + " outstanding GREFs. Performing a full GC!");
190189
System.GC.Collect ();
191190
}
192191

193-
return r;
194-
}
192+
if (!Logger.LogGlobalRef)
193+
return r;
195194

196-
static byte GetObjectRefType (JniObjectReferenceType type)
197-
{
198-
switch (type) {
199-
case JniObjectReferenceType.Invalid: return (byte) 'I';
200-
case JniObjectReferenceType.Local: return (byte) 'L';
201-
case JniObjectReferenceType.Global: return (byte) 'G';
202-
case JniObjectReferenceType.WeakGlobal: return (byte) 'W';
203-
default: return (byte) '*';
204-
}
195+
var ctype = GetObjectRefType (value.Type);
196+
var ntype = GetObjectRefType (r.Type);
197+
var tname = Thread.CurrentThread.Name;
198+
var tid = Thread.CurrentThread.ManagedThreadId;
199+
var from = new StringBuilder (new StackTrace (true).ToString ());
200+
RuntimeNativeMethods._monodroid_gref_log_new (value.Handle, ctype, r.Handle, ntype, tname, tid, from, 1);
201+
202+
return r;
205203
}
206204

207205
public override void DeleteGlobalReference (ref JniObjectReference value)
208206
{
209-
var log = Logger.LogGlobalRef;
210-
var ctype = log ? GetObjectRefType (value.Type) : (byte) '*';
211-
var tname = log ? Thread.CurrentThread.Name : null;
212-
var tid = log ? Thread.CurrentThread.ManagedThreadId : 0;
213-
var from = log ? new StringBuilder (new StackTrace (true).ToString ()) : null;
214-
RuntimeNativeMethods._monodroid_gref_log_delete (value.Handle, ctype, tname, tid, from, 1);
207+
Interlocked.Decrement (ref _grefc);
208+
209+
if (Logger.LogGlobalRef) {
210+
var ctype = GetObjectRefType (value.Type);
211+
var tname = Thread.CurrentThread.Name;
212+
var tid = Thread.CurrentThread.ManagedThreadId;
213+
var from = new StringBuilder (new StackTrace (true).ToString ());
214+
RuntimeNativeMethods._monodroid_gref_log_delete (value.Handle, ctype, tname, tid, from, 1);
215+
}
215216

216217
base.DeleteGlobalReference (ref value);
217218
}
218219

219220
public override JniObjectReference CreateWeakGlobalReference (JniObjectReference value)
220221
{
221222
var r = base.CreateWeakGlobalReference (value);
223+
Interlocked.Increment (ref _weak_grefc);
224+
225+
if (!Logger.LogGlobalRef)
226+
return r;
222227

223-
var log = Logger.LogGlobalRef;
224-
var ctype = log ? GetObjectRefType (value.Type) : (byte) '*';
225-
var ntype = log ? GetObjectRefType (r.Type) : (byte) '*';
226-
var tname = log ? Thread.CurrentThread.Name : null;
227-
var tid = log ? Thread.CurrentThread.ManagedThreadId : 0;
228-
var from = log ? new StringBuilder (new StackTrace (true).ToString ()) : null;
228+
var ctype = GetObjectRefType (value.Type);
229+
var ntype = GetObjectRefType (r.Type);
230+
var tname = Thread.CurrentThread.Name;
231+
var tid = Thread.CurrentThread.ManagedThreadId;
232+
var from = new StringBuilder (new StackTrace (true).ToString ());
229233
RuntimeNativeMethods._monodroid_weak_gref_new (value.Handle, ctype, r.Handle, ntype, tname, tid, from, 1);
230234

231235
return r;
232236
}
233237

234238
public override void DeleteWeakGlobalReference (ref JniObjectReference value)
235239
{
236-
var log = Logger.LogGlobalRef;
237-
var ctype = log ? GetObjectRefType (value.Type) : (byte) '*';
238-
var tname = log ? Thread.CurrentThread.Name : null;
239-
var tid = log ? Thread.CurrentThread.ManagedThreadId : 0;
240-
var from = log ? new StringBuilder (new StackTrace (true).ToString ()) : null;
241-
RuntimeNativeMethods._monodroid_weak_gref_delete (value.Handle, ctype, tname, tid, from, 1);
240+
Interlocked.Decrement (ref _weak_grefc);
241+
242+
if (Logger.LogGlobalRef) {
243+
var ctype = GetObjectRefType (value.Type);
244+
var tname = Thread.CurrentThread.Name;
245+
var tid = Thread.CurrentThread.ManagedThreadId;
246+
var from = new StringBuilder (new StackTrace (true).ToString ());
247+
RuntimeNativeMethods._monodroid_weak_gref_delete (value.Handle, ctype, tname, tid, from, 1);
248+
}
242249

243250
base.DeleteWeakGlobalReference (ref value);
244251
}
252+
253+
static byte GetObjectRefType (JniObjectReferenceType type)
254+
{
255+
switch (type) {
256+
case JniObjectReferenceType.Invalid: return (byte) 'I';
257+
case JniObjectReferenceType.Local: return (byte) 'L';
258+
case JniObjectReferenceType.Global: return (byte) 'G';
259+
case JniObjectReferenceType.WeakGlobal: return (byte) 'W';
260+
default: return (byte) '*';
261+
}
262+
}
245263
}
246264

247265
class AndroidTypeManager : JniRuntime.JniTypeManager {

0 commit comments

Comments
 (0)