-
Notifications
You must be signed in to change notification settings - Fork 566
Description
Android framework version
net10.0-android
Affected platform version
.NET10 android on physical device, Mono runtime
Description
We are seeing what appears to be excessive managed and native memory growth when repeatedly switching between dynamically created Android activities.
In our production app, we use many dynamic activities and frequent activity transitions. Activities are often recreated. To isolate the behavior, I built a minimal reproduction with two simple activities (ActivityA and ActivityB) that navigate back and forth. The UI is created entirely in code.
Minimal reproduction:
https://github.com/jdw89-123/activitiesFillHeap
Expected behavior
Repeated activity recreation and activity switching should remain stable and should not drive the managed or native heap into repeated full-heap GC / OOM scenarios.
Actual behavior
After repeated navigation between activities, the app shows:
- continuous growth of managed and native memory
- heavy GC pressure
- UI stalls
- eventually either:
OutOfMemoryErroror- ANR or
- the process gets killed by the system
Android Studio Live Telemetry shows rapid growth in both native and Java heap:
Managed heap analysis
Managed heap dumps (via dotnet-gcdump) show a continuous increase in the number of specific object types.
The object count:
- does not decrease after GC
- does not stabilize at a plateau
- instead grows steadily with repeated activity transitions
Affected types:
Android.Runtime.IdentityHashTargetsWeakReference<Java.Interop.IJavaPeerable>
Additional observation (explicit GC)
Based on the discussion in the following PR:
#10856
I experimented with triggering an explicit GC in OnCreate of the activity:
System.GC.WaitForPendingFinalizers();
System.GC.Collect();Notes
The reproduction is intentionally minimal, but it shows the same pattern we are seeing in a much larger production application.
What makes this particularly suspicious is that even this small repro appears sufficient to push both the managed and native heap into a steadily growing state that does not recover over time.
Additionally, the number of view elements on the activities appears to influence how quickly the issue manifests. Increasing the number of UI elements reduces the number of activity transitions required to reach the critical state, suggesting that the problem is related to the amount of UI objects created and destroyed during activity recreation.
Steps to Reproduce
-
Clone the minimal reproduction repository:
https://github.com/jdw89-123/activitiesFillHeap -
Build and deploy the app to a physical Android device.
-
Launch the app.
-
Repeatedly press the button to switch between ActivityA and ActivityB.
-
Continue switching activities continuously.
After approximately 2–3 minutes of rapid switching, the app enters a critical state and eventually crashes (OutOfMemoryError or process kill).
Did you find any workaround?
no
Relevant log output
Without explicit GC, the repro repeatedly reaches the managed heap limit and eventually throws `OutOfMemoryError`, including failures for very small allocations:
- `Clamp target GC heap from 152MB to 128MB`
- `0% free, 128MB/128MB`
- `Forcing collection of SoftReferences for 32B allocation`
- `Throwing OutOfMemoryError "Failed to allocate a 32 byte allocation ..."`
- stack includes `crc64408c12b9499ddfa7.ActivityA.n_onCreate(Native method)`
With explicit GC, the failure is delayed but not prevented. The app still reaches the heap limit and eventually becomes unresponsive or gets terminated:
- `Activity destroy timeout for ... ActivityB`
- `Clamp target GC heap from 151MB to 128MB`
- `0% free, 127MB/128MB`
- `WaitForGcToComplete blocked Alloc on HeapTrim for 1.603s`
- later: `Throwing OutOfMemoryError "Failed to allocate a 16400 byte allocation ..."`
These logs suggest that explicit GC changes the timing, but not the underlying failure mode.Full logcats:
logcat1.txt
logcat2.txt
UPDATE: .NET 8 vs .NET 10
I compared the same reproduction on .NET 8 and .NET 10, with explicit GC enabled in OnCreate of both activities:
System.GC.WaitForPendingFinalizers();
System.GC.Collect();The difference is significant.
Under .NET 8, the application behaves as expected and remains stable during repeated activity switching.

Under .NET 10, memory consumption grows aggressively under the same conditions. Even with explicit GC, the profiler shows clearly higher and continuously increasing memory usage compared to .NET 8.

This suggests that the behavior is not only related to the general activity recreation pattern itself, but that there may also be a regression or behavioral change between .NET 8 and .NET 10 in how Android-managed/native interop objects are retained or released.