Skip to content
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

Critical GREF Accumulation Issue with StaticLayout and String in Uno Platform on Android #18951

Open
NerijusN opened this issue Nov 28, 2024 · 6 comments · Fixed by #18956
Open
Labels
difficulty/tbd Categorizes an issue for which the difficulty level needs to be defined. kind/bug Something isn't working triage/untriaged Indicates an issue requires triaging or verification

Comments

@NerijusN
Copy link

Current behavior

We are experiencing a critical issue in our Android application using Uno Platform (version 4.10.39). After extended usage, the app enters a loop where the garbage collector (GC) struggles to free memory, and objects accumulate in the GREF table until it exceeds the limit, causing performance degradation and eventual crashes.

  1. GREF Table Growth:
    android/text/StaticLayout is the primary contributor:
    Logs indicate 30936+17+273=31226 references.
    java/lang/String is also significant:
    Logs show 8293+63+83=8439 references.
    Both grow steadily under normal app usage and are not released.
  2. GC Logs:
    Massive GC attempts are logged continuously:
[monodroid-gc] 46104 outstanding GREFs. Performing a full GC!
[ernal.tradezer] Explicit concurrent copying GC freed 10(47KB) AllocSpace objects, 0(0B) LOS objects, 32% free, 49MB/73MB, paused 32us total 130.155ms
[monodroid-gc] 46105 outstanding GREFs. Performing a full GC!

Despite frequent GC, the GREF count continues to grow.

  1. Reproduction Steps:
Set adb shell setprop debug.mono.max_grefc 10000.

Run an isolated page that updates a TextBlock with price changes.
The GREF limit is reached within minutes.

  1. Profiling:
    Attempts to profile have not provided actionable insights.
    Observations suggest StaticLayout and String instances are created frequently during TextBlock.Measure and layout calculations but are not cleaned up.

Impact

This issue significantly impacts the app’s stability and makes it unsustainable for production environments.

Request

Could you provide insights or guidance on addressing this GREF accumulation issue?
Are there known workarounds for managing StaticLayout or String references more effectively in the Uno Platform?

Expected behavior

GC works as expected and Android app never experiences ANR because of intense GC calls.

How to reproduce it (as minimally and precisely as possible)

No response

Workaround

No response

Works on UWP/WinUI

None

Environment

Uno.UI / Uno.UI.WebAssembly / Uno.UI.Skia, Uno.WinUI / Uno.WinUI.WebAssembly / Uno.WinUI.Skia

NuGet package version(s)

CollectionTracking 1.0.0
CollectionTracking.DynamicData 1.0.1
DynamicData 7.1.1
GeneratedSerializers.Json 1.0.0-dev.38
Analytics 3.8.0
FluentValidation.DependencyInjectionExtensions 9.5.4
Microsoft.AppCenter.Analytics 5.0.1
Microsoft.AppCenter.Crashes 5.0.1
Microsoft.Extensions.Hosting 3.1.0
Microsoft.Extensions.Http 8.0.1
Microsoft.Extensions.Localization.Abstractions 3.1.0
Microsoft.Extensions.Logging.Filter 1.1.2
Microsoft.Maui.Essentials 8.0.7
Serilog.Settings.Configuration 8.0.0
Serilog.Extensions.Hosting 8.0.0
Serilog.Sinks.File 5.0.0
System.Diagnostics.DiagnosticSource 8.0.0
Refit 8.0.0
Refit.Newtonsoft.Json 8.0.0
Scrutor 4.2.0
Portable.System.DateTimeOnly 8.0.1
Uno.UniversalImageLoader 1.9.37
Mono.AotProfiler.Android 9.0.0-preview1
Xamarin.Kotlin.StdLib.Jdk8 2.0.21.1
BiometryService 1.1.0
Uno.Fonts.Fluent 2.3.0
Uno.WinUI 4.10.39
Uno.WinUI.RemoteControl 4.10.39
Uno.UI.Adapter.Microsoft.Extensions.Logging 4.10.39
Uno.Microsoft.Xaml.Behaviors.Interactivity.WinUI 2.3.1-uno.2
Uno.Microsoft.Xaml.Behaviors.WinUI.Managed 2.3.1-uno.2
Reactive.Annex.Uno.WinUI 1.0.2
Microsoft.Extensions.Logging.Console 8.0.1
Chinook.BackButtonManager 1.1.4
Chinook.BackButtonManager.Uno.WinUI 1.1.4
Chinook.DataLoader.Uno.WinUI 1.2.0
Chinook.DataLoader.DynamicMvvm 1.2.0
Chinook.DynamicMvvm.Uno.WinUI 1.4.3
Chinook.DynamicMvvm 1.4.3
Chinook.DynamicMvvm.CollectionTracking 1.4.3
Chinook.DynamicMvvm.Reactive 1.4.3
Chinook.DynamicMvvm.FluentValidation 1.4.3
Serilog.Sinks.Xamarin 1.0.0
Uno.CodeGen 2.0.0-dev.10
Uno.Injectable 2.0.0-dev.10
Uno.SourceGenerationTasks 4.2.0
MallardMessageHandlers 1.2.0
Chinook.StackNavigation.Abstractions 2.1.2
Chinook.SectionsNavigation.Uno.WinUI 2.1.2
Chinook.SectionsNavigation.Reactive 2.1.2
MessageDialogService.Uno.WinUI 1.0.2
Uno.Material.WinUI 2.6.1
Uno.Toolkit.WinUI 3.0.4
Uno.Toolkit.WinUI.Material 3.0.4
Nventive.Persistence.Reactive 0.4.0-dev.43
Nventive.Persistence.Uno.WinUI 0.4.0-dev.43
Nventive.View.Uno.WinUI 0.5.0-dev.74
ExtendedSplashScreen.Uno.WinUI 1.0.2
Uno.WinUI.Lottie 4.10.39
Uno.CommunityToolkit.WinUI.DeveloperTools 7.1.100
Uno.CommunityToolkit.WinUI.UI.Controls 7.1.100
Uno.WinUI.Svg 4.10.39
Svg.Skia 2.0.0.2

Affected platforms

Android

IDE

Visual Studio 2022

IDE version

17.11.3

Relevant plugins

No response

Anything else we need to know?

No response

@NerijusN NerijusN added difficulty/tbd Categorizes an issue for which the difficulty level needs to be defined. kind/bug Something isn't working triage/untriaged Indicates an issue requires triaging or verification labels Nov 28, 2024
@jeromelaban
Copy link
Member

Thanks for the report.

This is likely an issue coming from Xamarin, which may be related to dotnet/android#9377 and dotnet/runtime#106410 (comment). You can try adjusting the GC bridge and see if it helps.

@NerijusN
Copy link
Author

NerijusN commented Nov 28, 2024

We updated our MONO_GC_PARAMS settings to:

MONO_GC_PARAMS=bridge-implementation=new,nursery-size=128m,soft-heap-limit=512m

This change provided some improvement, as the GC freeze now occurs slightly later compared to our previous configuration:

MONO_GC_PARAMS=bridge-implementation=tarjan,nursery-size=32m,soft-heap-limit=256m

However, the core issue persists, and the GC still freezes during intensive application usage. Any guidance or additional suggestions for tuning these parameters would be greatly appreciated.

And we are on .net8 already, converted dependent libraries to .net8 too.

@jeromelaban
Copy link
Member

Thanks. We'll try to reproduce.

@jeromelaban
Copy link
Member

jeromelaban commented Nov 28, 2024

@NerijusN Could you provide the code that you used to reproduce the issue in standalone app?

@NerijusN
Copy link
Author

NerijusN commented Dec 10, 2024

Would like to re-open this ticket due to persistent GREF issues involving the following objects:

Count Object Name MCW Type
30,936 android/text/StaticLayout Android.Text.StaticLayout
8,293 java/lang/String Java.Lang.String
5,198 android/graphics/Rect Android.Graphics.Rect

While the fix mentioned in PR #18956 addresses Java.Lang.String by implementing limits on the JavaStringCache, the issue with android/text/StaticLayout remains unresolved and contributes significantly to GREF accumulation.

Observations

We tested the benefits of the workaround provided in PR #18956 by calculating the accumulation of Java.Lang.String objects using the snippet below.

		private void InitializeJavaStringLogging()
		{
#if __ANDROID__
			var thread = new Thread(() =>
			{
				try
				{
					var javaStringCacheType = typeof(Application).Assembly.GetType("Microsoft.UI.Xaml.Controls.JavaStringCache");
					if (javaStringCacheType == null)
					{
						throw new Exception("JavaStringCache was not found.");
					}

					var tableField = javaStringCacheType.GetField("_table", BindingFlags.NonPublic | BindingFlags.Static);
					if (tableField == null)
					{
						throw new Exception("JavaStringCache._table was not found.");
					}

					var table = tableField.GetValue(null);
					if (table == null)
					{
						throw new Exception("JavaStringCache._table is null.");
					}

					var tableType = table.GetType();

					var countProp = tableType.GetProperty("Count");
					if (countProp == null)
					{
						throw new Exception("JavaStringCache._table.Count was not found.");
					}

					while (true)
					{
						var countVal = countProp.GetValue(table); // HashtableEx.Count returns a field, accessing it should be thread-safe, so we don't need to lock JavaStringCache._gate.
						if (countVal is not int count)
						{
							throw new Exception("JavaStringCache._table.Count is not System.Int32.");
						}

						Trace.WriteLine($"JavaStringCache._table.Count = {count}");

						Thread.Sleep(TimeSpan.FromSeconds(30));
					}
				}
				catch (Exception ex)
				{
					Trace.WriteLine($"Error in Java string workaround: {ex}");
				}
			})
			{
				Name = "Java String Cleaner (Workaround)",
				IsBackground = true
			};

			thread.Start();
#endif
		}

PR #18956 may successfully reduce the count of java/lang/String objects during heavy GC operations, as evidenced by logs showing a manageable count of 509 before the GC loop but I doubt it will be enough.

Concern

However, the GREF counts for android/text/StaticLayout are far greater—over 30,000 instances. This dwarfs the java/lang/String improvements and suggests that StaticLayout needs a similar approach or another resolution to handle its excessive references effectively.

+g+ grefc 8867 gwrefc 0 obj-handle 0xd9/I -> new-handle 0x26eaa/G from thread '(null)'(1)
   at Android.Runtime.AndroidObjectReferenceManager.CreateGlobalReference(JniObjectReference ) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/AndroidRuntime.cs:line 179
   at Java.Interop.JniObjectReference.NewGlobalRef() in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniObjectReference.cs:line 139
   at Android.Runtime.JNIEnv.NewGlobalRef(IntPtr ) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNIEnv.cs:line 436
   at Android.Runtime.AndroidValueManager.AddPeer(IJavaPeerable , IntPtr , JniHandleOwnership , IntPtr& ) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/AndroidRuntime.cs:line 689
   at Java.Lang.Object.SetHandle(IntPtr value, JniHandleOwnership transfer) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Java.Lang/Object.cs:line 252
   at Java.Lang.Object..ctor(IntPtr , JniHandleOwnership ) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Java.Lang/Object.cs:line 80
   at Android.Text.Layout..ctor(IntPtr , JniHandleOwnership ) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net8.0/android-34/mcw/Android.Text.Layout.cs:line 337
   at Android.Text.StaticLayout..ctor(IntPtr , JniHandleOwnership ) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net8.0/android-34/mcw/Android.Text.StaticLayout.cs:line 366
   at System.Reflection.RuntimeConstructorInfo.InternalInvoke(RuntimeConstructorInfo , Object , IntPtr* , Exception& )
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Constructor(Object obj, IntPtr* args)
   at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object , Span`1 , BindingFlags )
   at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object , BindingFlags , Binder , Object[] , CultureInfo )
   at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags , Binder , Object[] , CultureInfo )
   at System.Reflection.ConstructorInfo.Invoke(Object[] parameters)
   at Java.Interop.TypeManager.CreateProxy(Type , IntPtr , JniHandleOwnership ) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Java.Interop/TypeManager.cs:line 338
   at Java.Interop.TypeManager.CreateInstance(IntPtr , JniHandleOwnership , Type ) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Java.Interop/TypeManager.cs:line 311
   at Java.Lang.Object.GetObject(IntPtr , JniHandleOwnership , Type ) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Java.Lang/Object.cs:line 303
   at Java.Lang.Object._GetObject[StaticLayout](IntPtr , JniHandleOwnership ) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Java.Lang/Object.cs:line 289
   at Java.Lang.Object.GetObject[StaticLayout](IntPtr handle, JniHandleOwnership transfer) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Java.Lang/Object.cs:line 281
   at Android.Text.StaticLayout.Builder.Build() in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net8.0/android-34/mcw/Android.Text.StaticLayout.cs:line 61
   at Microsoft.UI.Xaml.Controls.TextBlock.LayoutBuilder.MakeLayout(Int32 , Int32 ) in C:\a\1\s\src\Uno.UI\UI\Xaml\Controls\TextBlock\TextBlock.Android.cs:line 757
   at Microsoft.UI.Xaml.Controls.TextBlock.LayoutBuilder.UpdateLayout(Size , Boolean ) in C:\a\1\s\src\Uno.UI\UI\Xaml\Controls\TextBlock\TextBlock.Android.cs:line 593
   at Microsoft.UI.Xaml.Controls.TextBlock.LayoutBuilder.Build() in C:\a\1\s\src\Uno.UI\UI\Xaml\Controls\TextBlock\TextBlock.Android.cs:line 544
   at Microsoft.UI.Xaml.Controls.TextBlock.UpdateLayout(LayoutBuilder& , Size , Boolean ) in C:\a\1\s\src\Uno.UI\UI\Xaml\Controls\TextBlock\TextBlock.Android.cs:line 436
   at Microsoft.UI.Xaml.Controls.TextBlock.ArrangeOverride(Size ) in C:\a\1\s\src\Uno.UI\UI\Xaml\Controls\TextBlock\TextBlock.Android.cs:line 381
   at Microsoft.UI.Xaml.FrameworkElement.FrameworkElementLayouter.ArrangeOverride(Size ) in C:\a\1\s\src\Uno.UI\UI\Xaml\FrameworkElement.cs:line 1028
   at Microsoft.UI.Xaml.Controls.Layouter.Arrange(Rect ) in C:\a\1\s\src\Uno.UI\UI\Xaml\Controls\Layouter\Layouter.cs:line 260
   at Microsoft.UI.Xaml.FrameworkElement.OnLayoutCore(Boolean , Int32 , Int32 , Int32 , Int32 , Boolean ) in C:\a\1\s\src\Uno.UI\UI\Xaml\FrameworkElement.Android.cs:line 257
   at Uno.UI.UnoViewGroup.n_OnLayoutCore_ZIIIIZ(IntPtr jnienv, IntPtr native__this, Boolean p0, Int32 p1, Int32 p2, Int32 p3, Int32 p4, Boolean p5) in C:\a\1\s\src\Uno.UI.BindingHelper.Android\obj\Release\net7.0-android\generated\src\Uno.UI.UnoViewGroup.cs:line 530
   at Android.Runtime.DynamicMethodNameCounter.12(IntPtr , IntPtr , Boolean , Int32 , Int32 , Int32 , Int32 , Boolean )
   at Java.Interop.JniNativeMethods.wrapper_native_indirect_void_intptr_intptr_intptr_intptr_intptr(IntPtr& , IntPtr , IntPtr , IntPtr , IntPtr , IntPtr )
   at Java.Interop.JniEnvironment.InstanceMethods.CallNonvirtualVoidMethod(JniObjectReference , JniObjectReference , JniMethodInfo , JniArgumentValue* ) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/obj/Release/net7.0/JniEnvironment.g.cs:line 20825
   at Java.Interop.JniPeerMembers.JniInstanceMethods.InvokeNonvirtualVoidMethod(String , IJavaPeerable , JniArgumentValue* ) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods_Invoke.cs:line 93
   at Android.Views.ViewGroup.Layout(Int32 , Int32 , Int32 , Int32 ) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net8.0/android-34/mcw/Android.Views.ViewGroup.cs:line 3384
   at Microsoft.UI.Xaml.Controls.Layouter.ArrangeChildOverride(View , Rect ) in C:\a\1\s\src\Uno.UI\UI\Xaml\Controls\Layouter\Layouter.Android.cs:line 61
   at Microsoft.UI.Xaml.Controls.Layouter.ArrangeChild(View , Rect ) in C:\a\1\s\src\Uno.UI\UI\Xaml\Controls\Layouter\Layouter.cs:line 476
   at Microsoft.UI.Xaml.FrameworkElement.FrameworkElementLayouter.ArrangeElement(View , Rect ) in C:\a\1\s\src\Uno.UI\UI\Xaml\FrameworkElement.cs:line 1024
   at Microsoft.UI.Xaml.FrameworkElement.ArrangeElement(View , Rect ) in C:\a\1\s\src\Uno.UI\UI\Xaml\FrameworkElement.cs:line 347
   at Microsoft.UI.Xaml.Controls.StackPanel.ArrangeOverride(Size ) in C:\a\1\s\src\Uno.UI\UI\Xaml\Controls\StackPanel\StackPanel.Layout.cs:line 161
   at Microsoft.UI.Xaml.FrameworkElement.FrameworkElementLayouter.ArrangeOverride(Size ) in C:\a\1\s\src\Uno.UI\UI\Xaml\FrameworkElement.cs:line 1028
   at Microsoft.UI.Xaml.Controls.Layouter.Arrange(Rect ) in C:\a\1\s\src\Uno.UI\UI\Xaml\Controls\Layouter\Layouter.cs:line 260
   at Microsoft.UI.Xaml.FrameworkElement.OnLayoutCore(Boolean , Int32 , Int32 , Int32 , Int32 , Boolean ) in C:\a\1\s\src\Uno.UI\UI\Xaml\FrameworkElement.Android.cs:line 257
   at Microsoft.UI.Xaml.Controls.Panel.OnLayoutCore(Boolean , Int32 , Int32 , Int32 , Int32 , Boolean ) in C:\a\1\s\src\Uno.UI\UI\Xaml\Controls\Panel\Panel.Android.cs:line 78
   at Uno.UI.UnoViewGroup.n_OnLayoutCore_ZIIIIZ(IntPtr jnienv, IntPtr native__this, Boolean p0, Int32 p1, Int32 p2, Int32 p3, Int32 p4, Boolean p5) in C:\a\1\s\src\Uno.UI.BindingHelper.Android\obj\Release\net7.0-android\generated\src\Uno.UI.UnoViewGroup.cs:line 530
   at Android.Runtime.DynamicMethodNameCounter.12(IntPtr , IntPtr , Boolean , Int32 , Int32 , Int32 , Int32 , Boolean )
   at Java.Interop.JniNativeMethods.wrapper_native_indirect_void_intptr_intptr_intptr_intptr_intptr(IntPtr& , IntPtr , IntPtr , IntPtr , IntPtr , IntPtr )
   at Java.Interop.JniEnvironment.InstanceMethods.CallNonvirtualVoidMethod(JniObjectReference , JniObjectReference , JniMethodInfo , JniArgumentValue* ) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/obj/Release/net7.0/JniEnvironment.g.cs:line 20825
   at Java.Interop.JniPeerMembers.JniInstanceMethods.InvokeNonvirtualVoidMethod(String , IJavaPeerable , JniArgumentValue* ) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods_Invoke.cs:line 93
   at Android.Views.ViewGroup.Layout(Int32 , Int32 , Int32 , Int32 ) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net8.0/android-34/mcw/Android.Views.ViewGroup.cs:line 3384
   at Microsoft.UI.Xaml.Controls.Layouter.ArrangeChildOverride(View , Rect ) in C:\a\1\s\src\Uno.UI\UI\Xaml\Controls\Layouter\Layouter.Android.cs:line 61
   at Microsoft.UI.Xaml.Controls.Layouter.ArrangeChild(View , Rect ) in C:\a\1\s\src\Uno.UI\UI\Xaml\Controls\Layouter\Layouter.cs:line 476
   at Microsoft.UI.Xaml.FrameworkElement.FrameworkElementLayouter.ArrangeElement(View , Rect ) in C:\a\1\s\src\Uno.UI\UI\Xaml\FrameworkElement.cs:line 1024
   at Microsoft.UI.Xaml.FrameworkElement.ArrangeElement(View , Rect ) in C:\a\1\s\src\Uno.UI\UI\Xaml\FrameworkElement.cs:line 347
   at Microsoft.UI.Xaml.Controls.Grid.ArrangeOverride(Size ) in C:\a\1\s\src\Uno.UI\UI\Xaml\Controls\Grid\Grid.cs:line 1358
   at Microsoft.UI.Xaml.FrameworkElement.FrameworkElementLayouter.ArrangeOverride(Size ) in C:\a\1\s\src\Uno.UI\UI\Xaml\FrameworkElement.cs:line 1028
   at Microsoft.UI.Xaml.Controls.Layouter.Arrange(Rect ) in C:\a\1\s\src\Uno.UI\UI\Xaml\Controls\Layouter\Layouter.cs:line 260
   at Microsoft.UI.Xaml.FrameworkElement.OnLayoutCore(Boolean , Int32 , Int32 , Int32 , Int32 , Boolean ) in C:\a\1\s\src\Uno.UI\UI\Xaml\FrameworkElement.Android.cs:line 257
   at Microsoft.UI.Xaml.Controls.Panel.OnLayoutCore(Boolean , Int32 , Int32 , Int32 , Int32 , Boolean ) in C:\a\1\s\src\Uno.UI\UI\Xaml\Controls\Panel\Panel.Android.cs:line 78
   at Uno.UI.UnoViewGroup.n_OnLayoutCore_ZIIIIZ(IntPtr jnienv, IntPtr native__this, Boolean p0, Int32 p1, Int32 p2, Int32 p3, Int32 p4, Boolean p5) in C:\a\1\s\src\Uno.UI.BindingHelper.Android\obj\Release\net7.0-android\generated\src\Uno.UI.UnoViewGroup.cs:line 530
   at Android.Runtime.DynamicMethodNameCounter.12(IntPtr , IntPtr , Boolean , Int32 , Int32 , Int32 , Int32 , Boolean )
   at Java.Interop.JniNativeMethods.wrapper_native_indirect_void_intptr_intptr_intptr_intptr_intptr(IntPtr& , IntPtr , IntPtr , IntPtr , IntPtr , IntPtr )
   at Java.Interop.JniEnvironment.InstanceMethods.CallNonvirtualVoidMethod(JniObjectReference , JniObjectReference , JniMethodInfo , JniArgumentValue* ) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/obj/Release/net7.0/JniEnvironment.g.cs:line 20825
   at Java.Interop.JniPeerMembers.JniInstanceMethods.InvokeNonvirtualVoidMethod(String , IJavaPeerable , JniArgumentValue* ) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods_Invoke.cs:line 93
   at Android.Views.ViewGroup.Layout(Int32 , Int32 , Int32 , Int32 ) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net8.0/android-34/mcw/Android.Views.ViewGroup.cs:line 3384
   at Uno.UI.Services.LayoutManager.Arrange() in C:\a\1\s\src\Uno.UI\Services\LayoutManager.Android.cs:line 60
   at Uno.UI.Dispatching.CoreDispatcher.DispatchItemsToChoreographer() in C:\a\1\s\src\Uno.UI.Dispatching\Core\CoreDispatcher.Android.cs:line 96
   at Uno.UI.Dispatching.CoreDispatcher.FrameCallbackImplementor.DoFrame(Int64 ) in C:\a\1\s\src\Uno.UI.Dispatching\Core\CoreDispatcher.Android.cs:line 159
   at Android.Views.Choreographer.IFrameCallbackInvoker.n_DoFrame_J(IntPtr jnienv, IntPtr native__this, Int64 frameTimeNanos) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net8.0/android-34/mcw/Android.Views.Choreographer.cs:line 97
   at Android.Runtime.DynamicMethodNameCounter.1(IntPtr , IntPtr , Int64 )

handle 0x26eaa; key_handle 0xef31505: Java Type: `android/text/StaticLayout`; MCW type: `Android.Text.StaticLayout`

Given the impact of StaticLayout on GREF counts, further investigation and action are required to address this issue comprehensively.

@jeromelaban jeromelaban reopened this Dec 10, 2024
@Xiaoy312 Xiaoy312 assigned Xiaoy312 and unassigned Xiaoy312 Dec 10, 2024
@dlbd
Copy link

dlbd commented Dec 13, 2024

We were able to get accurate GREF counts using dalvik.system.VMDebug.dumpReferenceTables. Previously reported counts were from analyzing grefs.txt (produced by running the app after adb shell setprop debug.mono.log gref) with a custom tool and are not entirely accurate.

Summary:
   4845 of crc6499cc3f8d6dc23bc6.Grid (4845 unique instances)
   3945 of crc6499cc3f8d6dc23bc6.Border (3945 unique instances)
   3513 of android.graphics.Path (3513 unique instances)
   3194 of crc6499cc3f8d6dc23bc6.ContentPresenter (3194 unique instances)
   2696 of android.text.StaticLayout (2696 unique instances)
   2482 of crc6499cc3f8d6dc23bc6.TextBlock (2482 unique instances)
   2366 of android.graphics.Matrix (2366 unique instances)
   2365 of android.text.BoringLayout$Metrics (2365 unique instances)
   2364 of android.text.BoringLayout (2364 unique instances)
   1588 of crc645d8252535e7ff47e.Rectangle (1588 unique instances)
   1379 of crc6499cc3f8d6dc23bc6.Viewbox (1379 unique instances)
   1076 of crc6499cc3f8d6dc23bc6.StackPanel (1076 unique instances)
    977 of java.lang.Class (659 unique instances)
    851 of crc6499cc3f8d6dc23bc6.Canvas (851 unique instances)
    829 of crc6499cc3f8d6dc23bc6.ListViewItem (829 unique instances)
    776 of crc645d8252535e7ff47e.Ellipse (776 unique instances)
    755 of crc6499cc3f8d6dc23bc6.Button (755 unique instances)
    737 of java.lang.String (736 unique instances)
    731 of crc645d8252535e7ff47e.Path (731 unique instances)
    730 of crc640fb3240b8c2fa91f.Ripple (730 unique instances)
    422 of crc6499cc3f8d6dc23bc6.ContentControl (422 unique instances)
    403 of crc641dd767a4b709a75b.PathControl (403 unique instances)
    333 of android.graphics.drawable.PaintDrawable (333 unique instances)
    327 of com.android.org.conscrypt.OpenSSLX509Certificate (325 unique instances)
    319 of crc6499cc3f8d6dc23bc6.ImplicitTextBlock (319 unique instances)
    306 of crc6499cc3f8d6dc23bc6.UnoViewHolder (306 unique instances)
   ....

It appears that GREFs to UI elements are retained after navigating away from a page. It's not just GREFs to TextBlocks and their StaticLayouts that are accumulating, it's all UI elements (Grids, Borders, etc).

We also found that finalizers of Pages (added for testing) or objects referenced by Pages are never called on Android, but are called on Windows. This could mean that GREFs are accumulating because Pages (and their controls) are not garbage collected.

Attached is a separate project that tries to reproduce this issue. FinalizeBug.zip

Test scenario 1: click "Go to page 1/2" a few times, then click "GC" twice. On Windows trace output contains ~FinalizeLogger() from MainPage, ~MainPage(). On Android it does not.
Test scenario 2: click "Go to page 1/2" a few times, then click "Dump refs" (available only on Android). Find Grid global reference count in VS output or logcat (scroll to global reference table dump:, Summary:). Repeat. The number will be growing after each navigation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
difficulty/tbd Categorizes an issue for which the difficulty level needs to be defined. kind/bug Something isn't working triage/untriaged Indicates an issue requires triaging or verification
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants