Skip to content

Commit 79d9533

Browse files
authored
[generator] Use GC.KeepAlive for reference type method parameters. (#722)
Context: #719 @brendanzagaeski has been investigating a Xamarin.Android app crash: JNI DETECTED ERROR IN APPLICATION: use of deleted global reference 0x3d86 from android.view.View crc64720bb2db43a66fe9.FragmentContainer.n_onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle) … at crc64720bb2db43a66fe9.FragmentContainer.n_onCreateView(Native method) at crc64720bb2db43a66fe9.FragmentContainer.onCreateView(FragmentContainer.java:41) This had been a head-scratcher, but we had a GREF log, so all should be clear, right? 09-10 17:56:48.280 10123 10123 I monodroid-gref: +g+ grefc 1141 gwrefc 0 obj-handle 0x79/I -> new-handle 0x3d86/G from thread '(null)'(1) 09-10 17:56:48.294 10123 10123 I monodroid-gref: +w+ grefc 1140 gwrefc 2 obj-handle 0x3d86/G -> new-handle 0x1e3/W from thread 'finalizer'(10123) 09-10 17:56:48.294 10123 10123 I monodroid-gref: -g- grefc 1139 gwrefc 2 handle 0x3d86/G from thread 'finalizer'(10123) The GREF log *wasn't* immediately clear: sure, the GREF was turned into a Weak-GREF, and the Weak-GREF was then collected, but none of this explained *how* were were using this deleted GREF. (We were at this state of affairs for months: we "know" we're using a deleted GREF, but we don't know *how* or *why*. It was a very hard to hit bug.) Eventually we had a ["that's funny"][0] event: *sure*, the GREF is deleted, but: 1. Why is it being deleted by the finalizer? 2. …14ms *after construction*? A [Garbage Collection][1] refresher may be in order, but in short: 1. All `Java.Lang.Object` subclasses are "bridged" objects. 2. During a GC, all "collectable" bridged objects are gathered. A collectable object is one in which nothing in the managed GC references the object. 3. Once the GC is complete, all gathered collectable bridged objects are passed to a "cross references" callback. The callback is called *outside* the "scope" of a GC; "the world" is *not* frozen, other threads may be executing. 4. The "cross references" callback is the `MonoGCBridgeCallbacks::cross_references` field provided provided to [`mono_gc_register_bridge_callbacks()`][2]. In a Xamarin.Android app, the "cross references" callback will "toggle" a JNI Global Reference to a JNI Weak Global Reference, invoke a Java-side GC, and then try to obtain a JNI Global Reference from the JNI Weak Global Reference. If a non-`NULL` pointer is returned, the bridged object is kept alive. If a `NULL` pointer is returned, the bridged object will be considered dead, and will be added to the Finalization Queue (as `Java.Lang.Object` has a finalizer). Thus, it seemed "funny" that within 14ms an instance was created, GC'd, and determined to be garbage, *especially* when we *knew* that this instance was being passed to Java, which we expected to retain the instance. (Yet wasn't…?) After much banging of heads, and the yeoman's work of creating a simplified and consistently reproducible test case, we *think* we know the cause of the crash. Consider our normal Managed-to-Java marshaling code, e.g. partial class MaterialButton { public unsafe MaterialButton (global::Android.Content.Context context) : base (IntPtr.Zero, JniHandleOwnership.DoNotTransfer) { const string __id = "(Landroid/content/Context;)V"; if (((global::Java.Lang.Object) this).Handle != IntPtr.Zero) return; try { JniArgumentValue* __args = stackalloc JniArgumentValue [1]; /* 1 */ __args [0] = new JniArgumentValue ((context == null) ? IntPtr.Zero : ((global::Java.Lang.Object) context).Handle); /* 2 */ var __r = _members.InstanceMethods.StartCreateInstance (__id, ((object) this).GetType (), __args); SetHandle (__r.Handle, JniHandleOwnership.TransferLocalRef); /* 3 */ _members.InstanceMethods.FinishCreateInstance (__id, this, __args); } finally { } } } At (1), `context.Handle` is -- a JNI GREF -- is stored into a `JniArgumentValue* __args` value, and at (3) `__args` is passed into JNI, which will presumably "Do Something" with that handle. However, nothing ties `context.Handle` to `context`, so from (2) onward, `context` *may* be eligible for garbage collection. See also Chris Brumme's [Lifetime, GC.KeepAlive, handle recycling][3] blog article. It's about .NET Framework, but the same fundamental multithreading concepts apply. The fix is to *ensure* that `context` is kept alive *for as long as* `context.Handle` will be used, i.e. across the JNI `_members.InstanceMethods.FinishCreateInstance()` call: partial class MaterialButton { public unsafe MaterialButton (global::Android.Content.Context context) : base (IntPtr.Zero, JniHandleOwnership.DoNotTransfer) { const string __id = "(Landroid/content/Context;)V"; if (((global::Java.Lang.Object) this).Handle != IntPtr.Zero) return; try { JniArgumentValue* __args = stackalloc JniArgumentValue [1]; /* 1 */ __args [0] = new JniArgumentValue ((context == null) ? IntPtr.Zero : ((global::Java.Lang.Object) context).Handle); /* 2 */ var __r = _members.InstanceMethods.StartCreateInstance (__id, ((object) this).GetType (), __args); SetHandle (__r.Handle, JniHandleOwnership.TransferLocalRef); /* 3 */ _members.InstanceMethods.FinishCreateInstance (__id, this, __args); } finally { /* 4 */ global::System.GC.KeepAlive (context); } } } This should prevent e.g. `context` from being prematurely GC'd, which in turn should prevent the `JNI DETECTED ERROR` message. Update `tools/generator` to emit `GC.KeepAlive()` statements for every parameter type which isn't a value type (`enum`, `int`, `string`, etc.). `string` is considered a value type because we always send a "deep copy" of the string contents, so it won't matter if the `string` instance is GC'd immediately. [0]: https://quoteinvestigator.com/2015/03/02/eureka-funny/ [1]: https://docs.microsoft.com/xamarin/android/internals/garbage-collection [2]: http://docs.go-mono.com/?link=xhtml%3adeploy%2fmono-api-gc.html [3]: https://docs.microsoft.com/archive/blogs/cbrumme/lifetime-gc-keepalive-handle-recycling
1 parent 1a19ec0 commit 79d9533

25 files changed

+79
-0
lines changed

tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteCtorWithStringOverload.txt

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
_members.InstanceMethods.FinishCreateInstance (__id, this, __args);
1818
} finally {
1919
JNIEnv.DeleteLocalRef (native_mystring);
20+
global::System.GC.KeepAlive (mystring);
2021
}
2122
}
2223

@@ -38,6 +39,7 @@
3839
_members.InstanceMethods.FinishCreateInstance (__id, this, __args);
3940
} finally {
4041
JNIEnv.DeleteLocalRef (native_mystring);
42+
global::System.GC.KeepAlive (mystring);
4143
}
4244
}
4345

tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteKotlinUnsignedArrayTypeMethodsClass.txt

+4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public partial class MyClass {
2727
JNIEnv.CopyArray (native_value, value);
2828
JNIEnv.DeleteLocalRef (native_value);
2929
}
30+
global::System.GC.KeepAlive (value);
3031
}
3132
}
3233

@@ -46,6 +47,7 @@ public partial class MyClass {
4647
JNIEnv.CopyArray (native_value, value);
4748
JNIEnv.DeleteLocalRef (native_value);
4849
}
50+
global::System.GC.KeepAlive (value);
4951
}
5052
}
5153

@@ -65,6 +67,7 @@ public partial class MyClass {
6567
JNIEnv.CopyArray (native_value, value);
6668
JNIEnv.DeleteLocalRef (native_value);
6769
}
70+
global::System.GC.KeepAlive (value);
6871
}
6972
}
7073

@@ -84,6 +87,7 @@ public partial class MyClass {
8487
JNIEnv.CopyArray (native_value, value);
8588
JNIEnv.DeleteLocalRef (native_value);
8689
}
90+
global::System.GC.KeepAlive (value);
8791
}
8892
}
8993

tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteKotlinUnsignedArrayTypePropertiesClass.txt

+4
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public partial class MyClass {
3636
JNIEnv.CopyArray (native_value, value);
3737
JNIEnv.DeleteLocalRef (native_value);
3838
}
39+
global::System.GC.KeepAlive (value);
3940
}
4041
}
4142
}
@@ -65,6 +66,7 @@ public partial class MyClass {
6566
JNIEnv.CopyArray (native_value, value);
6667
JNIEnv.DeleteLocalRef (native_value);
6768
}
69+
global::System.GC.KeepAlive (value);
6870
}
6971
}
7072
}
@@ -94,6 +96,7 @@ public partial class MyClass {
9496
JNIEnv.CopyArray (native_value, value);
9597
JNIEnv.DeleteLocalRef (native_value);
9698
}
99+
global::System.GC.KeepAlive (value);
97100
}
98101
}
99102
}
@@ -123,6 +126,7 @@ public partial class MyClass {
123126
JNIEnv.CopyArray (native_value, value);
124127
JNIEnv.DeleteLocalRef (native_value);
125128
}
129+
global::System.GC.KeepAlive (value);
126130
}
127131
}
128132
}

tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/XAJavaInterop1-NRT/WriteCtorWithStringOverload.txt

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
_members.InstanceMethods.FinishCreateInstance (__id, this, __args);
1818
} finally {
1919
JNIEnv.DeleteLocalRef (native_mystring);
20+
global::System.GC.KeepAlive (mystring);
2021
}
2122
}
2223

@@ -38,6 +39,7 @@
3839
_members.InstanceMethods.FinishCreateInstance (__id, this, __args);
3940
} finally {
4041
JNIEnv.DeleteLocalRef (native_mystring);
42+
global::System.GC.KeepAlive (mystring);
4143
}
4244
}
4345

tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/XAJavaInterop1/WriteCtorWithStringOverload.txt

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
_members.InstanceMethods.FinishCreateInstance (__id, this, __args);
1818
} finally {
1919
JNIEnv.DeleteLocalRef (native_mystring);
20+
global::System.GC.KeepAlive (mystring);
2021
}
2122
}
2223

@@ -38,6 +39,7 @@
3839
_members.InstanceMethods.FinishCreateInstance (__id, this, __args);
3940
} finally {
4041
JNIEnv.DeleteLocalRef (native_mystring);
42+
global::System.GC.KeepAlive (mystring);
4143
}
4244
}
4345

tests/generator-Tests/expected.ji/Adapters/Xamarin.Test.AbsSpinner.cs

+2
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ static void n_SetAdapter_Lxamarin_test_SpinnerAdapter_ (IntPtr jnienv, IntPtr na
8383
__args [0] = new JniArgumentValue ((value == null) ? IntPtr.Zero : ((global::Java.Lang.Object) value).Handle);
8484
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, __args);
8585
} finally {
86+
global::System.GC.KeepAlive (value);
8687
}
8788
}
8889
}
@@ -126,6 +127,7 @@ public AbsSpinnerInvoker (IntPtr handle, JniHandleOwnership transfer) : base (ha
126127
_members.InstanceMethods.InvokeAbstractVoidMethod (__id, this, __args);
127128
} finally {
128129
JNIEnv.DeleteLocalRef (native_value);
130+
global::System.GC.KeepAlive (value);
129131
}
130132
}
131133
}

tests/generator-Tests/expected.ji/Adapters/Xamarin.Test.AdapterView.cs

+1
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ public AdapterViewInvoker (IntPtr handle, JniHandleOwnership transfer) : base (h
110110
_members.InstanceMethods.InvokeAbstractVoidMethod (__id, this, __args);
111111
} finally {
112112
JNIEnv.DeleteLocalRef (native_value);
113+
global::System.GC.KeepAlive (value);
113114
}
114115
}
115116
}

tests/generator-Tests/expected.ji/Core_Jar2Xml/Android.Text.SpannableString.cs

+3
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public unsafe SpannableString (global::Java.Lang.ICharSequence source)
4949
_members.InstanceMethods.FinishCreateInstance (__id, this, __args);
5050
} finally {
5151
JNIEnv.DeleteLocalRef (native_source);
52+
global::System.GC.KeepAlive (source);
5253
}
5354
}
5455

@@ -70,6 +71,7 @@ public unsafe SpannableString (string source)
7071
_members.InstanceMethods.FinishCreateInstance (__id, this, __args);
7172
} finally {
7273
JNIEnv.DeleteLocalRef (native_source);
74+
global::System.GC.KeepAlive (source);
7375
}
7476
}
7577

@@ -102,6 +104,7 @@ static int n_GetSpanFlags_Ljava_lang_Object_ (IntPtr jnienv, IntPtr native__this
102104
var __rm = _members.InstanceMethods.InvokeVirtualInt32Method (__id, this, __args);
103105
return (global::Android.Text.SpanTypes) __rm;
104106
} finally {
107+
global::System.GC.KeepAlive (what);
105108
}
106109
}
107110

tests/generator-Tests/expected.ji/Core_Jar2Xml/Android.Text.SpannableStringInternal.cs

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ static int n_GetSpanFlags_Ljava_lang_Object_ (IntPtr jnienv, IntPtr native__this
6060
var __rm = _members.InstanceMethods.InvokeVirtualInt32Method (__id, this, __args);
6161
return (global::Android.Text.SpanTypes) __rm;
6262
} finally {
63+
global::System.GC.KeepAlive (p0);
6364
}
6465
}
6566

tests/generator-Tests/expected.ji/Core_Jar2Xml/Android.Views.View.cs

+3
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ public virtual unsafe void SetOnClickListener (global::Android.Views.View.IOnCli
176176
__args [0] = new JniArgumentValue ((l == null) ? IntPtr.Zero : ((global::Java.Lang.Object) l).Handle);
177177
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, __args);
178178
} finally {
179+
global::System.GC.KeepAlive (l);
179180
}
180181
}
181182

@@ -206,6 +207,7 @@ public virtual unsafe void SetOn123Listener (global::Android.Views.View.IOnClick
206207
__args [0] = new JniArgumentValue ((l == null) ? IntPtr.Zero : ((global::Java.Lang.Object) l).Handle);
207208
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, __args);
208209
} finally {
210+
global::System.GC.KeepAlive (l);
209211
}
210212
}
211213

@@ -238,6 +240,7 @@ public virtual unsafe void AddTouchables (global::System.Collections.Generic.ILi
238240
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, __args);
239241
} finally {
240242
JNIEnv.DeleteLocalRef (native_views);
243+
global::System.GC.KeepAlive (views);
241244
}
242245
}
243246

tests/generator-Tests/expected.ji/GenericArguments/Com.Google.Android.Exoplayer.Drm.FrameworkMediaDrm.cs

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ public unsafe void SetOnEventListener (global::Com.Google.Android.Exoplayer.Drm.
5858
__args [0] = new JniArgumentValue ((p0 == null) ? IntPtr.Zero : ((global::Java.Lang.Object) p0).Handle);
5959
_members.InstanceMethods.InvokeAbstractVoidMethod (__id, this, __args);
6060
} finally {
61+
global::System.GC.KeepAlive (p0);
6162
}
6263
}
6364

tests/generator-Tests/expected.ji/NestedTypes/Xamarin.Test.NotificationCompatBase.cs

+1
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ public unsafe InstanceInner (global::Xamarin.Test.NotificationCompatBase __self)
186186
SetHandle (__r.Handle, JniHandleOwnership.TransferLocalRef);
187187
_members.InstanceMethods.FinishCreateInstance (__id, this, __args);
188188
} finally {
189+
global::System.GC.KeepAlive (__self);
189190
}
190191
}
191192

tests/generator-Tests/expected.ji/NormalMethods/Xamarin.Test.SomeObject.cs

+5
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public unsafe SomeObject (global::Java.Lang.Class c)
4747
SetHandle (__r.Handle, JniHandleOwnership.TransferLocalRef);
4848
_members.InstanceMethods.FinishCreateInstance (__id, this, __args);
4949
} finally {
50+
global::System.GC.KeepAlive (c);
5051
}
5152
}
5253

@@ -109,6 +110,8 @@ static int n_Handle_Ljava_lang_Object_Ljava_lang_Throwable_ (IntPtr jnienv, IntP
109110
var __rm = _members.InstanceMethods.InvokeVirtualInt32Method (__id, this, __args);
110111
return __rm;
111112
} finally {
113+
global::System.GC.KeepAlive (o);
114+
global::System.GC.KeepAlive (t);
112115
}
113116
}
114117

@@ -255,6 +258,7 @@ public virtual unsafe void VoidMethodWithParams (string astring, int anint, glob
255258
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, __args);
256259
} finally {
257260
JNIEnv.DeleteLocalRef (native_astring);
261+
global::System.GC.KeepAlive (anObject);
258262
}
259263
}
260264

@@ -318,6 +322,7 @@ public virtual unsafe void ArrayListTest (global::System.Collections.Generic.ILi
318322
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, __args);
319323
} finally {
320324
JNIEnv.DeleteLocalRef (native_p0);
325+
global::System.GC.KeepAlive (p0);
321326
}
322327
}
323328

tests/generator-Tests/expected.ji/NormalProperties/Xamarin.Test.SomeObject.cs

+1
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ public override unsafe int SomeInteger {
210210
__args [0] = new JniArgumentValue ((value == null) ? IntPtr.Zero : ((global::Java.Lang.Object) value).Handle);
211211
_members.InstanceMethods.InvokeAbstractVoidMethod (__id, this, __args);
212212
} finally {
213+
global::System.GC.KeepAlive (value);
213214
}
214215
}
215216
}

tests/generator-Tests/expected.ji/ParameterXPath/Xamarin.Test.A.cs

+2
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public virtual unsafe void SetA (global::Java.Lang.Object adapter)
6060
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, __args);
6161
} finally {
6262
JNIEnv.DeleteLocalRef (native_adapter);
63+
global::System.GC.KeepAlive (adapter);
6364
}
6465
}
6566

@@ -92,6 +93,7 @@ public virtual unsafe void ListTest (global::System.Collections.Generic.IList<gl
9293
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, __args);
9394
} finally {
9495
JNIEnv.DeleteLocalRef (native_p0);
96+
global::System.GC.KeepAlive (p0);
9597
}
9698
}
9799

tests/generator-Tests/expected.ji/StaticProperties/Xamarin.Test.SomeObject.cs

+1
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ public static unsafe void SetSomeObject (global::Java.Lang.Object newvalue)
102102
__args [0] = new JniArgumentValue ((newvalue == null) ? IntPtr.Zero : ((global::Java.Lang.Object) newvalue).Handle);
103103
_members.StaticMethods.InvokeVoidMethod (__id, __args);
104104
} finally {
105+
global::System.GC.KeepAlive (newvalue);
105106
}
106107
}
107108

tests/generator-Tests/expected.ji/Streams/Java.IO.FilterOutputStream.cs

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public unsafe FilterOutputStream (global::System.IO.Stream @out)
4949
_members.InstanceMethods.FinishCreateInstance (__id, this, __args);
5050
} finally {
5151
JNIEnv.DeleteLocalRef (native__out);
52+
global::System.GC.KeepAlive (@out);
5253
}
5354
}
5455

tests/generator-Tests/expected.ji/Streams/Java.IO.InputStream.cs

+2
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ public virtual unsafe int Read (byte[] buffer)
216216
JNIEnv.CopyArray (native_buffer, buffer);
217217
JNIEnv.DeleteLocalRef (native_buffer);
218218
}
219+
global::System.GC.KeepAlive (buffer);
219220
}
220221
}
221222

@@ -257,6 +258,7 @@ public virtual unsafe int Read (byte[] buffer, int byteOffset, int byteCount)
257258
JNIEnv.CopyArray (native_buffer, buffer);
258259
JNIEnv.DeleteLocalRef (native_buffer);
259260
}
261+
global::System.GC.KeepAlive (buffer);
260262
}
261263
}
262264

tests/generator-Tests/expected.ji/Streams/Java.IO.OutputStream.cs

+2
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ public virtual unsafe void Write (byte[] buffer)
136136
JNIEnv.CopyArray (native_buffer, buffer);
137137
JNIEnv.DeleteLocalRef (native_buffer);
138138
}
139+
global::System.GC.KeepAlive (buffer);
139140
}
140141
}
141142

@@ -175,6 +176,7 @@ public virtual unsafe void Write (byte[] buffer, int offset, int count)
175176
JNIEnv.CopyArray (native_buffer, buffer);
176177
JNIEnv.DeleteLocalRef (native_buffer);
177178
}
179+
global::System.GC.KeepAlive (buffer);
178180
}
179181
}
180182

tests/generator-Tests/expected.ji/TestInterface/Test.ME.GenericImplementation.cs

+1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ public virtual unsafe void SetObject (byte[] value)
8282
JNIEnv.CopyArray (native_value, value);
8383
JNIEnv.DeleteLocalRef (native_value);
8484
}
85+
global::System.GC.KeepAlive (value);
8586
}
8687
}
8788

tests/generator-Tests/expected.ji/TestInterface/Test.ME.GenericObjectPropertyImplementation.cs

+1
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ static void n_SetObject_Ljava_lang_Object_ (IntPtr jnienv, IntPtr native__this,
101101
__args [0] = new JniArgumentValue ((value == null) ? IntPtr.Zero : ((global::Java.Lang.Object) value).Handle);
102102
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, __args);
103103
} finally {
104+
global::System.GC.KeepAlive (value);
104105
}
105106
}
106107
}

tests/generator-Tests/expected.ji/TestInterface/Test.ME.GenericStringImplementation.cs

+1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ public virtual unsafe void SetObject (string[] value)
8282
JNIEnv.CopyArray (native_value, value);
8383
JNIEnv.DeleteLocalRef (native_value);
8484
}
85+
global::System.GC.KeepAlive (value);
8586
}
8687
}
8788

tests/generator-Tests/expected.ji/TestInterface/Test.ME.TestInterfaceImplementation.cs

+3
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ public override unsafe int GetSpanFlags (global::Java.Lang.Object tag)
179179
var __rm = _members.InstanceMethods.InvokeAbstractInt32Method (__id, this, __args);
180180
return __rm;
181181
} finally {
182+
global::System.GC.KeepAlive (tag);
182183
}
183184
}
184185

@@ -194,6 +195,7 @@ public override unsafe void Append (global::Java.Lang.ICharSequence value)
194195
_members.InstanceMethods.InvokeAbstractVoidMethod (__id, this, __args);
195196
} finally {
196197
JNIEnv.DeleteLocalRef (native_value);
198+
global::System.GC.KeepAlive (value);
197199
}
198200
}
199201

@@ -210,6 +212,7 @@ public override unsafe void Append (global::Java.Lang.ICharSequence value)
210212
return global::Java.Lang.Object.GetObject<Java.Lang.ICharSequence> (__rm.Handle, JniHandleOwnership.TransferLocalRef);
211213
} finally {
212214
JNIEnv.DeleteLocalRef (native_value);
215+
global::System.GC.KeepAlive (value);
213216
}
214217
}
215218

tests/generator-Tests/expected.ji/java.lang.Enum/Java.Lang.Enum.cs

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public unsafe int CompareTo (global::Java.Lang.Object o)
4444
return __rm;
4545
} finally {
4646
JNIEnv.DeleteLocalRef (native_o);
47+
global::System.GC.KeepAlive (o);
4748
}
4849
}
4950

0 commit comments

Comments
 (0)