[Java.Interop] Avoid locks for cached JNI member lookups - #12377
Open
jonathanpeppers wants to merge 1 commit into
Open
[Java.Interop] Avoid locks for cached JNI member lookups#12377jonathanpeppers wants to merge 1 commit into
jonathanpeppers wants to merge 1 commit into
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR improves Java.Interop JNI member lookup performance by removing monitor-lock overhead from cached method/field ID access in JniPeerMembers, and adds an Android on-device BenchmarkDotNet instrumentation app to measure JNI invocation changes.
Changes:
- Replace lock-protected
Dictionarycaches withConcurrentDictionarycaches for JNI instance/static method and field lookups. - Preserve serialized first-time field resolution while making cached reads lock-free.
- Add a new instrumentation-only Android BenchmarkDotNet app and wire it into the test solution.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| Xamarin.Android-Tests.slnx | Adds the new Android benchmarks project to the tests solution. |
| tests/Android.Benchmarks/README.md | Documents how to run the on-device BenchmarkDotNet instrumentation benchmarks. |
| tests/Android.Benchmarks/JniMethodInfoBenchmarks.cs | Adds benchmarks comparing cached JNI invocation vs generated binding calls. |
| tests/Android.Benchmarks/BenchmarkInstrumentation.cs | Implements the Android instrumentation entry point that runs BenchmarkDotNet in-process. |
| tests/Android.Benchmarks/AndroidManifest.xml | Provides a minimal manifest for the instrumentation-only benchmark app. |
| tests/Android.Benchmarks/Android.Benchmarks.csproj | Defines the Android benchmark app project and BenchmarkDotNet dependency. |
| external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JniPeerMembersTests.cs | Updates test reflection expectations to match ConcurrentDictionary caches. |
| external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniStaticMethods.cs | Switches static method cache to ConcurrentDictionary and removes monitor locking on cached reads. |
| external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniStaticFields.cs | Switches static field cache to ConcurrentDictionary and adjusts the synchronization strategy. |
| external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs | Switches instance method + subclass-constructor caches to ConcurrentDictionary and removes monitor locking on cached reads. |
| external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceFields.cs | Switches instance field cache to ConcurrentDictionary and adjusts the synchronization strategy. |
Use ConcurrentDictionary with static GetOrAdd factories for the JniPeerMembers instance and static method and field caches, plus subclass constructor dispatch. Cached JNI member lookups no longer enter monitor locks, while existing remapping, fallback, exception, and lifetime behavior is preserved. Add an instrumentation-only Android BenchmarkDotNet app for measuring JNI member lookup and invocation changes on-device. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 45264ff0-e493-4402-a76f-576afd88ee70
jonathanpeppers
force-pushed
the
jonathanpeppers-cache-jni-method-ids
branch
from
August 13, 2026 16:37
66f4b4e to
5d91fc5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
JniPeerMemberscached JNI method and field IDs in dictionaries protected bymonitor locks. Every generated binding invocation paid that lock cost even
after the JNI member had already been resolved.
Use
ConcurrentDictionaryfor the instance/static method and field caches,plus subclass constructor dispatch. Cached reads no longer enter a monitor,
while first field resolution remains serialized and existing method
replacement, fallback, exception, and lifetime behavior is preserved.
This also adds an instrumentation-only Android BenchmarkDotNet app for
measuring JNI invocation changes on-device.
Related to #11885.
Performance
Pixel 5, .NET 11 CoreCLR:
Pixel 5 MAUI/CoreCLR cold startup, 40 counterbalanced pairs built from
identical restored packages:
Paired changed-minus-base delta: -2.77 ms, with a 95% confidence
interval of -7.61 to +2.06 ms.
apkdiffreports:Testing
Java.Interop managed build
JniPeerMembersTests: 8 passed, 1 skippedAndroid BenchmarkDotNet instrumentation run
40-pair counterbalanced MAUI/CoreCLR cold-start comparison
apkdiffpackage comparisonUseful description of why the change is necessary
Links to related issues
Unit tests