Skip to content

[Mono.Android] follow up remaining value manager activation fixes#11115

Closed
simonrozsival wants to merge 3 commits intodev/peppers/fix-activate-gref-leakfrom
dev/simonrozsival/11101-valuemanager-followup
Closed

[Mono.Android] follow up remaining value manager activation fixes#11115
simonrozsival wants to merge 3 commits intodev/peppers/fix-activate-gref-leakfrom
dev/simonrozsival/11101-valuemanager-followup

Conversation

@simonrozsival
Copy link
Copy Markdown
Member

@simonrozsival simonrozsival commented Apr 15, 2026

Follow-up to #11112.

This PR fixes:

  • JavaMarshalValueManager.ActivateViaReflection()
  • SimpleValueManager.ActivateViaReflection()

jonathanpeppers and others added 2 commits April 14, 2026 14:09
Fixes: #11101
Fixes: #10989

Context: 5c23bcda8 (PR #9640)

The Java.Interop Unification (5c23bcd) changed `Object` to extend
`JavaObject`, introducing an additional `ConstructPeer` call in the
constructor chain. `TypeManager.Activate` was updated to use
`SetPeerReference` but never set the `Activatable` state flag.
Without it, each `ConstructPeer` call in the constructor chain
(`JavaObject()` and `Object.SetHandle()`) creates a new JNI global
ref, overwriting the previous one without deleting it — leaking 3
global refs per `LayoutInflater.Inflate` call.

Before the fix, the new test fails with:

    Global reference leak detected: 30 extra global refs after
    inflating/GC'ing 10 custom views. Before=207, After=237

This went unnoticed because the `Activate` path is only triggered
when Java creates .NET objects (not the other way around). The two
main scenarios are Activity recreation and custom C# views in Android
XML layouts. Most developers use .NET MAUI, which has a single
Activity and does not use custom C# views in Android layout XML files,
so neither scenario was commonly hit.

Changes:

  - Promote `GetUninitializedObject` from a local function in
    `CreateProxy` to a shared static method. It sets
    `Activatable | Replaceable`, which tells `ConstructPeer` to
    return early and not create duplicate global refs.

  - Have `Activate` call `GetUninitializedObject` then
    `ConstructPeer` to create one global ref while `jobject` is
    still a valid JNI local ref. The `Activatable` flag then prevents
    duplicates during the constructor chain.

  - Add regression test that inflates custom views and asserts JNI
    global reference count does not grow.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 15, 2026 08:50
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Follow-up runtime + generator changes to close remaining Java→managed activation paths that could recreate the same peer and leak JNI global references, plus additional test coverage to prevent regressions.

Changes:

  • Update reflection-based activation paths to create a proper global ref via ConstructPeer() before invoking managed constructors, with Activatable|Replaceable state set upfront.
  • Add JavaPeerProxy.ConstructActivatedPeer() and emit calls to it in the trimmable typemap generator for inherited-ctor activation/UCO paths.
  • Add/extend tests to validate gref-leak behavior and ensure the generator references the new helper.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/Mono.Android-Tests/Mono.Android-Tests/Android.Widget/CustomWidgetTests.cs Adds a regression test asserting that inflating custom views doesn’t leak global refs.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs Extends generator tests to assert the new helper is referenced.
src/Mono.Android/Microsoft.Android.Runtime/SimpleValueManager.cs Fixes reflection activation to set state and construct the peer before invoking ctors.
src/Mono.Android/Microsoft.Android.Runtime/JavaMarshalValueManager.cs Same activation fix as SimpleValueManager for the JavaMarshal path.
src/Mono.Android/Java.Interop/TypeManager.cs Adjusts legacy activation to use uninitialized object + ConstructPeer() before ctor invocation.
src/Mono.Android/Java.Interop/JavaPeerProxy.cs Introduces ConstructActivatedPeer() helper for generated proxy activation paths.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs Emits calls to ConstructActivatedPeer() in inherited-ctor CreateInstance/UCO constructor IL.

Comment thread src/Mono.Android/Java.Interop/JavaPeerProxy.cs Outdated
@simonrozsival simonrozsival changed the title [Mono.Android] follow up remaining activation peer leak fixes [Mono.Android] follow up remaining value manager activation fixes Apr 15, 2026
…opilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/11101-valuemanager-followup branch from 798dfeb to d49eaab Compare April 15, 2026 09:02
@jonathanpeppers jonathanpeppers force-pushed the dev/peppers/fix-activate-gref-leak branch from c64fdb9 to 31a44fa Compare April 15, 2026 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants