Skip to content

Frequent explicit Java GC during scrolling when using NativeAOT #11567

@vyacheslav-volkov

Description

@vyacheslav-volkov

Android framework version

net10.0-android

Affected platform version

NET 10.0

Description

I am seeing a significant difference in Java/ART GC behavior between a regular Mono-based Android build and a NativeAOT Android build.

When the application is built without NativeAOT and runs on the Mono runtime, scrolling works smoothly and explicit Java GC is rare during normal scrolling. When it does happen, it usually frees several megabytes of Java heap, so the collection appears to be justified.

However, when the same application is built with NativeAOT, scrolling causes frequent explicit Java/ART GC invocations. These collections appear to be mostly useless according to logcat output: the Java heap is around 49–50% free, only a very small amount of memory is freed, but each GC cycle still takes around 50–95ms total time.

This seems to correlate with visible micro-stutters during scrolling, especially on weaker Android devices.

During scrolling in the NativeAOT build, logcat shows repeated Explicit concurrent copying GC entries like this:

2026-06-03 11:55:00.382  6356-6404  I  Explicit concurrent copying GC freed 24767(1208KB) AllocSpace objects, 0(0B) LOS objects, 49% free, 15MB/30MB, paused 36us,55us total 66.541ms
2026-06-03 11:55:02.971  6356-6404  I  Explicit concurrent copying GC freed 9771(532KB) AllocSpace objects, 0(0B) LOS objects, 49% free, 15MB/30MB, paused 67us,56us total 53.427ms
2026-06-03 11:55:04.101  6356-6404  I  Explicit concurrent copying GC freed 5453(348KB) AllocSpace objects, 0(0B) LOS objects, 49% free, 15MB/31MB, paused 29us,49us total 49.779ms
2026-06-03 11:55:05.795  6356-6404  I  Explicit concurrent copying GC freed 5715(337KB) AllocSpace objects, 0(0B) LOS objects, 49% free, 15MB/31MB, paused 63us,54us total 62.454ms
2026-06-03 11:55:07.041  6356-6404  I  Explicit concurrent copying GC freed 7200(463KB) AllocSpace objects, 0(0B) LOS objects, 49% free, 15MB/31MB, paused 54us,50us total 62.270ms
2026-06-03 11:55:08.949  6356-6404  I  Explicit concurrent copying GC freed 2224(254KB) AllocSpace objects, 1(20KB) LOS objects, 49% free, 15MB/31MB, paused 80us,56us total 79.183ms
2026-06-03 11:55:10.754  6356-6404  I  Explicit concurrent copying GC freed 873(144KB) AllocSpace objects, 1(20KB) LOS objects, 49% free, 15MB/31MB, paused 49us,54us total 79.568ms

The concerning part is that these are explicit Java GC calls, but they do not seem justified by memory pressure:

49–50% free
15MB / 30–31MB heap
only ~128KB–1.2MB freed per collection
total GC time around 50–95ms

Mono behavior

In the non-NativeAOT Mono build, explicit Java GC is much less frequent during the same scrolling scenario. When it happens, it frees several megabytes of memory, so it looks much more justified:

2026-06-03 12:04:50.800  28412-28412  I  Explicit concurrent copying GC freed 171675(6883KB) AllocSpace objects, 8(448KB) LOS objects, 49% free, 16MB/33MB, paused 32us,45us total 57.721ms
2026-06-03 12:06:44.926  28412-28412  I  Explicit concurrent copying GC freed 97654(4619KB) AllocSpace objects, 38(1288KB) LOS objects, 50% free, 16MB/33MB, paused 34us,42us total 53.758ms
2026-06-03 12:07:15.847  28412-28412  I  Explicit concurrent copying GC freed 138894(6556KB) AllocSpace objects, 25(612KB) LOS objects, 50% free, 17MB/34MB, paused 69us,50us total 64.408ms

So the difference is not just that NativeAOT logs explicit Java GC. The difference is that under Mono the GC is rare and frees around 5–7MB per collection, while under NativeAOT it can happen every 1–2 seconds during scrolling and often frees only a few hundred KB.

Is there any way to disable or tune this behavior?

Steps to Reproduce

  1. Create or use a .NET 10 Android application with a scrollable UI, for example a RecyclerView with enough items to continuously scroll.
  2. Build and run the application with and without NativeAOT.
  3. Open the screen with the scrollable list and scroll for 1–2 minutes and check the difference.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area: App RuntimeIssues in `libmonodroid.so`.needs-triageIssues that need to be assigned.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions