From a674a16fbc8009f4b93fbc78667c4596018ac3ce Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Fri, 7 Mar 2025 11:38:12 -0500 Subject: [PATCH] [Hello-Java.Base] MonoVM + peer collection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Context: a5b229daa3538ea9ad06d22541e7bfa0ae747d9d Now that MonoVM can be used on the desktop (a5b229da), let's show it in action! Update `samples/Hello-Java.Base` to call `GC.Collect()` and `GC.WaitForPendingFinalizers()` to help cause the `MyJLO` instance to be collected before app exit. Which is fine, but what we also need are *GREF logs*. Unfortunately, we can't use `JreRuntimeOptions.JniGlobalReferenceWriter` for that purpose, because `libjava-interop.dylib` is native code and we want both managed and native code to write GREF logs to the same place. Instead, set the `JAVA_INTEROP_GREF_LOG` environment variable to a file path: % JAVA_INTEROP_GREF_LOG=g.txt ./samples/Hello-Java.Base/bin/Release/osx-x64/publish/Hello-Java.Base MonoVM support enabled # jonp: LoadJvmLibrary(/Library/Java/JavaVirtualMachines/microsoft-17.jdk/Contents/Home/lib/libjli.dylib)=1164832560 # jonp: JNI_CreateJavaVM=4554903296; JNI_GetCreatedJavaVMs=4554903376 # jonp: executing JNI_CreateJavaVM=10f7e4f00 # jonp: r=0 javavm=111647690 jnienv=7fbfb7afbaa8 WARNING in native method: JNI call made without checking exceptions when required to from CallStaticObjectMethodV binding? net.dot.jni.sample.MyJLO@575bab75 WARNING in native method: JNI call made without checking exceptions when required to from CallVoidMethodV Then read `g.txt`. Selected output includes: +g+ grefc 5 gwrefc 0 obj-handle 0x7fbfb6104920/L -> new-handle 0x7fbfb57274c0/G from thread '(null)'(4) at Java.Interop.MonoRuntimeObjectReferenceManager.CreateGlobalReference(JniObjectReference reference) at Java.Interop.JniObjectReference.NewGlobalRef() at Java.Interop.JniRuntime.JniValueManager.ConstructPeer(IJavaPeerable peer, JniObjectReference& reference, JniObjectReferenceOptions options) at Java.Interop.JavaObject.Construct(JniObjectReference& reference, JniObjectReferenceOptions options) at Java.Lang.Object..ctor() at Hello.MyJLO..ctor() at Hello.App.<>c.b__3_0() at System.Threading.Thread.StartCallback() Created PeerReference=0x7fbfb57274c0/G IdentityHashCode=0x575bab75 Instance=0xef12a08e Instance.Type=Hello.MyJLO, Java.Type=net/dot/jni/sample/MyJLO *take_weak obj=0x10fca0478; handle=0x7fbfb57274c0 +w+ grefc 7 gwrefc 1 obj-handle 0x7fbfb57274c0/G -> new-handle 0x7fbf9570a801/W from thread '(null)'(1) take_weak_global_ref_jni -g- grefc 6 gwrefc 1 handle 0x7fbfb57274c0/G from thread '(null)'(1) take_weak_global_ref_jni *try_take_global obj=0x10fca0478 -> wref=0x7fbf9570a801 handle=0x0 -w- grefc 6 gwrefc 0 handle 0x7fbf9570a801/W from thread '(null)'(1) take_global_ref_jni Finalizing PeerReference=0x0/G IdentityHashCode=0x575bab75 Instance=0xef12a08e Instance.Type=Hello.MyJLO Which shows the creation of JNI handle 0x7fbfb57274c0/G, which is associated with `Hello.MyJLO`, and then we go through the GC process: * Create a JNI Weak Global Reference 0x7fbf9570a801/W (`+w+ …`) * Delete the JNI Global Reference 0x7fbfb57274c0/G (`-g- …`) * Perform a Java-side GC (not mentioned) * Try to create a JNI Global Reference from the Weak Global Reference (`*try_take_global …`) * Delete the JNI Weak Global Reference (`-w- …`) * Finalize the `Hello.MyJLO` instance (`Finalizing PeerReference=0x0/G …`) The `Instance=` value can be used to correlate instances; it's the value of `RuntimeHelpers.GetHashCode()`. --- samples/Hello-Java.Base/Program.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/samples/Hello-Java.Base/Program.cs b/samples/Hello-Java.Base/Program.cs index ea23ff85e..7b5355e3a 100644 --- a/samples/Hello-Java.Base/Program.cs +++ b/samples/Hello-Java.Base/Program.cs @@ -38,13 +38,7 @@ public static void Main (string[] args) v => reportTiming = v != null }, { "v|verbosity:", $"Set console log verbosity to {{LEVEL}}. Default is 0.", - (int? v) => { - verbosity = v.HasValue ? v.Value : verbosity + 1; - if (verbosity > 0) { - logger = CreateConsoleLogger (); - } - } - }, + (int? v) => verbosity = v.HasValue ? v.Value : verbosity + 1 }, { "h|help", "Show this message and exit.", v => showHelp = v != null }, @@ -54,6 +48,9 @@ public static void Main (string[] args) options.WriteOptionDescriptions (Console.Out); return; } + if (verbosity > 0) { + logger = CreateConsoleLogger (); + } var builder = new JreRuntimeOptions () { JniAddNativeMethodRegistrationAttributePresent = true, JvmLibraryPath = jvmPath ?? global::Java.InteropTests.TestJVM.GetJvmLibraryPath (logger), @@ -79,6 +76,11 @@ public static void Main (string[] args) } CreateJLO (); + + GC.Collect (); + GC.Collect (); + GC.WaitForPendingFinalizers (); + GC.WaitForPendingFinalizers (); } static Action CreateConsoleLogger ()