Skip to content

Commit 3cd8310

Browse files
committed
[Java.Interop] Make JniEnvironment static; add JniTransition.
Recall from commit 25de1f3: > 1. @jassmith has been profiling Xamarin.Android, and GC allocations > would be "a problem", as increased allocations will increase > GC-related overheads, slowing down the app. > > We want a "GC steady state" wherein once things are running, > and things are cached, future JNI invocations such as > JNIEnv::CallObjectMethod() won't allocate additional garbage > (unless *Java* is returning new "garbage" instances...) We want a GC "steady state", meaning NO NEW OPTIONAL ALLOCATIONS. ("Required" allocations would include new wrappers for Java instances that entered managed code for the first time.) Which brings us to JniEnvironment, which has always been a non-`static` reference type and in commit 2f9fece required an allocation on every Java-to-managed transition: TReturnType (*)(IntPtr jnienv, IntPtr context /* ... args ....*/ ) { var __envp = new JniEnvironment (jnienv); try { ... } catch (Exception e) { __envp.SetPendingException (e); } finally { __envp.Dispose (); } } That `new JniEnvironment` is required so that we have a "scope" around which to dispose of allocated JniLocalReference instances, alongside the previous requirements of checking the JNIEnv* pointer for invalidation/etc (which JniEnvironment.CheckCurrent() did). We *want* a data structure for Java-to-managed invocations. We *don't* want a GC allocation. Enter the JniTransition struct, which is a subset of JniEnvironment, but a struct, so it implies no GC allocations: public struct JniTransition : IDisposable { public JniTransition (IntPtr environmentPointer); public void SetPendingException (Exception exception); public void Dispose (); } Use is identical to the previous JniEnvironment: TReturnType (*)(IntPtr jnienv, IntPtr context /* ... args ....*/ ) { var __envp = new JniTransition(jnienv); try { ... } catch (Exception e) { __envp.SetPendingException (e); } finally { __envp.Dispose (); } } The one conceptual problem is that JniTransition IS a mutable struct, and mutable structs are EVIL (e6e1365), so why is this acceptable? It's acceptable because NOBODY SHOULD EVER USE IT. It would be used by a binding generator and the runtime marshal method generators; it shouldn't need to be used by developers, much less *copied*, which is where the real issues with mutable structs come in. With JniTransition in place, turn JniEnvironment into a `static` class. This has the unfortunate effect of breaking *everything*, but it has the benefit that we're no longer creating JniEnvironmentInvoker multiple times on a single thread (and probably re-caching the delegates for the same method OVER AND OVER AND...). As such, JniEnvironment.Current.JavaVM becomes JniEnvironment.Runtime (I'm finding `JavaVM` more annoying the more I look at it), and the other useful instance members become static members. We also introduce JniEnvironmentInfo, which contains the per-thread JniEnvironment information. Ideally this would be internal, but it needs to be public so that JniObjectReferenceManager can use it for JNI Local Reference accounting; *something* per-thread needs to be updated for JNI local references...
1 parent 0065738 commit 3cd8310

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1679
-1713
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,9 +257,9 @@ have it call into a runtime method that performs the work *at runtime*:
257257
[Dynamic]
258258
static IntPtr n_Clone (IntPtr jnienv, IntPtr native__this)
259259
{
260-
JniEnvironment __envp = new JniEnvironment (jnienv);
260+
JniTransition __envp = new JniTransition (jnienv);
261261
try {
262-
var __jvm = __envp.JavaVM;
262+
var __jvm = JniEnvironment.Runtime;
263263
return __jvm.CallObjectMethod (native__this, "Clone");
264264
}
265265
catch (Exception __e) {
@@ -283,7 +283,7 @@ all these shims with *real* method bodies:
283283
// Post-build generated code
284284
static IntPtr n_Clone (IntPtr jnienv, IntPtr native__this)
285285
{
286-
JniEnvironment __envp = new JniEnvironment (jnienv);
286+
JniTransition __envp = new JniTransition (jnienv);
287287
try {
288288
var __jvm = __envp.JavaVM;
289289
var __this = __jvm.GetObject<ExportTest>(native__this);

samples/Hello/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public static unsafe void Main (string[] args)
2020
}
2121
Console.WriteLine ("Part 2!");
2222
using (var vm = new JreVMBuilder ().CreateJreVM ()) {
23-
Console.WriteLine ("# JniEnvironment.Current={0}", JniEnvironment.Current);
23+
Console.WriteLine ("# JniEnvironment.EnvironmentPointer={0}", JniEnvironment.EnvironmentPointer);
2424
Console.WriteLine ("vm.SafeHandle={0}", vm.InvocationPointer);
2525
var t = new JniType ("java/lang/Object");
2626
var c = t.GetConstructor ("()V");

src/Android.Interop/Java.Interop/AndroidVM.cs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Reflection;
34

45
using Android.Runtime;
56

@@ -9,19 +10,22 @@ namespace Java.Interop {
910
public delegate IntPtr SafeHandleDelegate_CallObjectMethodA (IntPtr env, IntPtr instance, IntPtr method, JValue[] args);
1011
public delegate void SafeHandleDelegate_DeleteLocalRef (IntPtr env, IntPtr handle);
1112

13+
delegate int JNIEnv_GetJavaVM (IntPtr jnienv, out IntPtr vm);
14+
15+
1216
class AndroidVMBuilder : JavaVMOptions {
1317

1418
public AndroidVMBuilder ()
1519
{
20+
var GetJavaVM = (JNIEnv_GetJavaVM) Delegate.CreateDelegate (
21+
typeof(JNIEnv_GetJavaVM),
22+
typeof(JNIEnv).GetMethod ("GetJavaVM", BindingFlags.NonPublic | BindingFlags.Static));
23+
IntPtr invocationPointer;
24+
GetJavaVM (JNIEnv.Handle, out invocationPointer);
25+
1626
EnvironmentPointer = JNIEnv.Handle;
1727
NewObjectRequired = ((int) Android.OS.Build.VERSION.SdkInt) <= 10;
18-
using (var env = new JniEnvironment (JNIEnv.Handle)) {
19-
IntPtr vm;
20-
int r = JniEnvironment.References.GetJavaVM (out vm);
21-
if (r < 0)
22-
throw new InvalidOperationException ("JNIEnv::GetJavaVM() returned: " + r);
23-
InvocationPointer = vm;
24-
}
28+
InvocationPointer = invocationPointer;
2529
ObjectReferenceManager = Java.InteropTests.LoggingJniObjectReferenceManagerDecorator.GetObjectReferenceManager (new JniObjectReferenceManager ());
2630
}
2731

src/Android.Interop/Tests/TestsSample.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ unsafe void GetJICallObjectMethodAndDeleteLocalRefTimings (
136136
var usafeDel = (IntPtrDelegate_DeleteLocalRef)
137137
Marshal.GetDelegateForFunctionPointer (JNIEnv_DeleteLocalRef, typeof (IntPtrDelegate_DeleteLocalRef));
138138

139-
var uh = JniEnvironment.Current.EnvironmentPointer;
139+
var uh = JniEnvironment.EnvironmentPointer;
140140
var args = new JValue [0];
141141

142142
sw.Restart ();

src/Java.Interop.Dynamic/Java.Interop.Dynamic/DynamicJavaClass.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ struct JniArgumentMarshalInfo {
154154
internal JniArgumentMarshalInfo (object value, Type valueType)
155155
{
156156
this = new JniArgumentMarshalInfo ();
157-
var jvm = JniEnvironment.Current.JavaVM;
157+
var jvm = JniEnvironment.Runtime;
158158
var info = jvm.GetJniMarshalInfoForType (valueType);
159159
if (info.CreateJValue != null)
160160
jvalue = info.CreateJValue (value);

src/Java.Interop.Dynamic/Java.Interop.Dynamic/JavaClassInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ Dictionary<string, List<JavaFieldInfo>> LookupFields ()
227227

228228
var n_type = Field_getType.InvokeVirtualObjectMethod (field);
229229
using (var type = new JniType (ref n_type, JniObjectReferenceOptions.DisposeSourceReference)) {
230-
var info = JniEnvironment.Current.JavaVM.GetJniTypeInfoForJniTypeReference (type.Name);
230+
var info = JniEnvironment.Runtime.GetJniTypeInfoForJniTypeReference (type.Name);
231231
overloads.Add (new JavaFieldInfo (Members, name + "\u0000" + info.QualifiedReference, isStatic));
232232
}
233233

@@ -321,7 +321,7 @@ internal unsafe bool TryInvokeMember (IJavaPeerable self, JavaMethodBase[] overl
321321
static List<JniType> GetJniTypes (DynamicMetaObject[] args)
322322
{
323323
var r = new List<JniType> (args.Length);
324-
var vm = JniEnvironment.Current.JavaVM;
324+
var vm = JniEnvironment.Runtime;
325325
foreach (var a in args) {
326326
try {
327327
var at = new JniType (vm.GetJniTypeInfoForType (a.LimitType).QualifiedReference);

src/Java.Interop.Dynamic/Java.Interop.Dynamic/JavaConstructorInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public override unsafe object Invoke (IJavaPeerable self, JValue* arguments)
4242
{
4343
if (self == null) {
4444
var h = members.InstanceMethods.StartCreateInstance (JniSignature, typeof (JavaInstanceProxy), arguments);
45-
self = JniEnvironment.Current.JavaVM.GetObject<JavaInstanceProxy> (ref h, JniObjectReferenceOptions.DisposeSourceReference);
45+
self = JniEnvironment.Runtime.GetObject<JavaInstanceProxy> (ref h, JniObjectReferenceOptions.DisposeSourceReference);
4646
}
4747
members.InstanceMethods.FinishCreateInstance (JniSignature, self, arguments);
4848
return new DynamicJavaInstance (self);

src/Java.Interop.Dynamic/Java.Interop.Dynamic/JavaMemberInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ protected virtual void Dispose (bool disposing)
2222

2323
protected static object ToReturnValue (ref JniObjectReference handle, string signature, int n)
2424
{
25-
var instance = JniEnvironment.Current.JavaVM.GetObject (ref handle, JniObjectReferenceOptions.DisposeSourceReference);
25+
var instance = JniEnvironment.Runtime.GetObject (ref handle, JniObjectReferenceOptions.DisposeSourceReference);
2626
switch (signature [n]) {
2727
case 'L':
2828
return new DynamicJavaInstance (instance);

src/Java.Interop.Dynamic/Java.Interop.Dynamic/JavaMethodBase.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public void LookupArguments ()
7373
if (arguments != null)
7474
return;
7575

76-
var vm = JniEnvironment.Current.JavaVM;
76+
var vm = JniEnvironment.Runtime;
7777
var sb = new StringBuilder ();
7878

7979
if (!IsConstructor) {
@@ -111,7 +111,7 @@ public bool CompatibleWith (List<JniType> args, DynamicMetaObject[] dargs)
111111
if (args.Count != arguments.Count)
112112
return false;
113113

114-
var vm = JniEnvironment.Current.JavaVM;
114+
var vm = JniEnvironment.Runtime;
115115

116116
for (int i = 0; i < arguments.Count; ++i) {
117117
if (args [i] == null) {

src/Java.Interop.Dynamic/Java.Interop.Dynamic/JavaMethodInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ protected override string JniReturnType {
5454
get {
5555
if (ReturnType == null)
5656
return "V";
57-
return JniEnvironment.Current.JavaVM.GetJniTypeInfoForJniTypeReference (ReturnType.Name).QualifiedReference;
57+
return JniEnvironment.Runtime.GetJniTypeInfoForJniTypeReference (ReturnType.Name).QualifiedReference;
5858
}
5959
}
6060

0 commit comments

Comments
 (0)