在我的应用程序中,当在 ContentPage (CP1) 上按下按钮时,我创建一个新的 ContentPage (CP2) 并将其推送到堆栈上,就像这样......
await Navigation.PushAsync(new CP2(param1, param2));
CP2 消耗大量内存,约 400MB。当显示 CP2 时,如果我单击后退箭头导航回 CP1,则不会释放内存。每次我从 CP1 导航到 CP2 时,当我从导航堆栈中弹出 CP2 时,会分配另外约 400MB 且不会释放。该应用程序很快就会因内存使用过多而被设备抛弃。 CP2 不订阅除其自身控件生成的事件之外的任何事件。它确实使用自定义构建的 ContentView。但是,该视图不订阅任何事件,也不使用任何非托管资源。
这是 CP2 的 xaml(实际名称为 MyWaxBoxAddItemsV):
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:custom_components="clr-namespace:FasterWaxer.CustomComponents"
x:Class="FasterWaxer.MyWaxBoxAddItemsV"
Title="MyWaxBoxAddItemsV">
<CollectionView Grid.Row="0" ItemsSource="{Binding BrandCatalog}" Margin="7,0,5,0">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid RowDefinitions="65" ColumnDefinitions="*,60">
<!--<custom_components:CatalogItem Grid.Column="0" />-->
<Grid Grid.Column="1" VerticalOptions="Center" WidthRequest="50" HeightRequest="50" HorizontalOptions="End">
<Grid.GestureRecognizers>
<TapGestureRecognizer Tapped="OnAddWaxBoxItemTapped" CommandParameter="{Binding}"/>
</Grid.GestureRecognizers>
<Image Source="{Binding AddActionIconFileName}" Margin="0,0,5,0" VerticalOptions="Center" HeightRequest="23" WidthRequest="28" HorizontalOptions="End" />
</Grid>
<BoxView HeightRequest="1" Grid.ColumnSpan="2" VerticalOptions="End"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage>
下面是定义 CP2 订阅的唯一事件处理程序的代码:
public partial class MyWaxBoxAddItemsV : ContentPage
{
private readonly MyWaxBoxAddItemsVM vm;
public MyWaxBoxAddItemsV(MyWaxBoxVM waxBox, WaxBrandCatalogItemVM brandCatalog)
{
vm = new MyWaxBoxAddItemsVM(waxBox, brandCatalog);
InitializeComponent();
BindingContext = vm;
}
private async void OnAddWaxBoxItemTapped(object sender, TappedEventArgs e)
{
if (e.Parameter == null) return;
var item = (WaxCatalogItemVM)e.Parameter;
if (item.IsInMyWaxBox)
{
string prompt = new($"Remove {item.BrandName} {item.ModelName}?");
if (await DisplayAlert(prompt, string.Empty, "Remove", "Cancel"))
{
vm.WaxBox.RemoveItem(item);
}
}
else
{
vm.WaxBox.AddItem(item);
}
return;
}
}
custom_components:CatalogItem 是一个 ContentView,显示一堆格式化数据。你可以看到,我在上面的xaml中注释掉了。当我这样做时,每次渲染 CP2 时的内存消耗都会减少。然而,内存仍然没有被释放。
MyWaxBoxAddItemsVM(“CP2”的 ViewModel)中有 ObservableObjects 和 ObservableCollections。 CP2 MyWaxBoxAddItemsVM 层次结构中没有事件处理程序。
我花了几个小时翻阅 Microsoft 文章、stackoverflow 帖子等。我看到了很多与使用 IDisposable 和其他解决方案相关的信息,这些信息几乎都与取消订阅事件处理程序相关。就我而言,我没有任何我知道的可以取消订阅的事件处理程序。其他类中的事件处理程序可以更改属于 CP2 ViewModel 一部分的 ObservableObject。我一直在想我的问题可能与此有关。 关于为什么从导航堆栈中弹出 CP2 时内存没有被释放的任何指导?这是否与 CP2 的层次结构中包含 ObservableObjects 有关? CP2 弹出时如何释放内存?
7 月 2 日:添加有关我尝试过的其他内容的信息...
我尝试将 MyWaxBoxAddItemsV (CP2) 实现为 IDisposable。 Dispose() 将 vm 和 BindingContext 设置为 null。在创建 CP2 的新实例之前,CP1 对 CP2 的前一个实例调用 MyWaxBoxAddItemsV.Dispose()。这不会导致内存消耗模式发生变化。
一如既往,我将非常感谢您的指导,帮助我找到解决方案。
我刚刚搜索了“毛伊岛内存管理”,发现了几个与我遇到的可怕内存泄漏相关的错误报告。您可以在这里看到它们:滚动时 CollectionView 中的大量内存泄漏,到处都有内存泄漏,[核心]修复绑定中的内存泄漏。
据称最新的 .NET/MAUI 预览版 (8.0.0-preview.5.8529) 中已包含修复程序。我安装了此 SDK,但是,我使用的是最新版本的 Visual Studio for Mac,它与 .NET 8.0 不兼容。所以,我还无法看到问题是否已经解决。一旦我了解更多信息或找到解决方案,我会立即向这里报告。
我最终使用了 Adam Essenmacher 的 AdamE.MemoryToolkit.Maui。它对我来说是一个救星(应用程序保护程序)。解决了我所有的内存泄漏问题。我刚刚迁移到 .NET 9.0,必须看看内存问题是否仍然存在。