Skip to content

[Bug] Memory Leak in Toast due to unreleased Animation subscriptions in ControlTheme #869

@xoma-zver

Description

@xoma-zver

I have encountered a significant memory leak when using the Toast system. When displaying and closing a large number of toasts, the memory usage grows continuously and is not reclaimed by the GC.

Using dotMemory, I identified that ResourceDictionary and ControlTheme are retaining thousands of StyleInstance and DisposeAnimationInstanceSubject objects, which in turn keep the ToastCard controls alive.

Reproduction Steps

  1. Open ToastDemoViewModel.cs.
  2. To simplify the experiment, modify the ShowNormal method (change to async void and add a loop):
    for (int i = 0; i < 1000; i++)
    {
        ToastManager?.Show(
            new Toast("This is message"),
            showIcon: ShowIcon,
            showClose: ShowClose,
            type: notificationType);
    
        await Task.Delay(10);
    }
  3. Run the Ursa.Demo project.
  4. Click the Information button.
  5. Observe the memory usage.

Results
Memory usage grows by hundreds of MBs and is never released.

Image Image

Probable Cause
The issue lies in Ursa.Themes.Semi/Controls/Toast.axaml.

Currently, animations are applied via a nested Style targeting a template part (PART_LayoutTransformControl) with FillMode="Forward".

<Style Selector="^ /template/ LayoutTransformControl#PART_LayoutTransformControl">
    <Style.Animations>
        <Animation
            Easing="QuadraticEaseIn"
            FillMode="Forward"
            Duration="0:0:0.3">
...
        </Animation>
        <Animation
            Easing="QuadraticEaseIn"
            FillMode="Forward"
            Duration="0:0:0.15">
...
        </Animation>
    </Style.Animations>
</Style>

It seems that StyleInstance created for the template part fails to release the animation subscription when the control is removed from the visual tree, causing the leak.

I compared this implementation with NotificationCard.xaml from the standard Avalonia Fluent Theme:

Avalonia.Themes.Fluent/Controls/NotificationCard

In the Fluent theme, the entrance animation is defined directly in ControlTheme.Animations (targeting the control itself, not a template part):

    <ControlTheme.Animations>
      <Animation Duration="0:0:0.45" Easing="QuadraticEaseIn" FillMode="Forward">
...
      </Animation>
    </ControlTheme.Animations>

Proposed Fix

If we move the animation from the nested style selector directly to the root ControlTheme.Animations, the garbage collection works as expected.

Image

However, this change definitely changes the animation's behavior, and if it's expected and satisfactory, we can apply this fix. Otherwise, need to explore other options.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions