Skip to content

[Hello-Java.Base] MonoVM + peer collection #1319

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 7, 2025

Conversation

jonpryor
Copy link
Contributor

@jonpryor jonpryor commented Mar 7, 2025

Context: a5b229d

Now that MonoVM can be used on the desktop (a5b229d), 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.<CreateJLO>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().

Context: a5b229d

Now that MonoVM can be used on the desktop (a5b229d), 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.<CreateJLO>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()`.
@jonpryor jonpryor merged commit 93c3872 into main Mar 7, 2025
2 checks passed
@jonpryor jonpryor deleted the dev/jonp/jonp-try-collect-peer-in-Hello-Java.Base branch March 7, 2025 17:24
jonpryor pushed a commit to dotnet/android that referenced this pull request Mar 10, 2025
Changes: dotnet/java-interop@6096f8b...93c3872

  * dotnet/java-interop@93c38729: [Hello-Java.Base] MonoVM + peer collection (dotnet/java-interop#1319)
  * dotnet/java-interop@a5b229da: [java-interop] Resuscitate MonoVM GC Bridge (dotnet/java-interop#1316)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
@github-actions github-actions bot locked and limited conversation to collaborators Apr 7, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant