【问题标题】:WPF & Prism 4.1 Garbage Collection / Memory IssuesWPF 和 Prism 4.1 垃圾收集/内存问题
【发布时间】:2013-02-24 06:25:09
【问题描述】:

我使用 WPF、.Net 4、Prism 4.1 和 Unity 构建了一个 Prism 应用程序。我正在使用 DirectoryModuleCatalog 在运行时查找模块。我的视图显示在 TabControl (MainRegion) 中。当我从该区域中删除视图时,视图和视图模型仍保留在内存中并且永远不会被垃圾收集 - tabitem 被删除。经过几个小时的搜索,我无法弄清楚我做错了什么。

这是我的引导程序:

public class Bootstrapper : UnityBootstrapper
{
    protected override void InitializeShell()
    {
        base.InitializeShell();
        App.Current.MainWindow = (Window)Shell;
        App.Current.MainWindow.Show();
    }

    protected override DependencyObject CreateShell()
    {
        var shell = new Shell();
        return shell;
    }

    protected override IModuleCatalog CreateModuleCatalog()
    {
        return new DirectoryModuleCatalog() { ModulePath = @".\Modules" };
    }
}

这是我的模块:

[Module(ModuleName = "ModuleA")]
public class Module : IModule
{
    private IRegionManager _regionManager;

    public Module(IRegionManager regionManager)
    {
        _regionManager = regionManager;
    }

    public void Initialize()
    {
        var view = new UserControl1();
        //_regionManager.RegisterViewWithRegion("MainRegion", typeof(UserControl1));
        _regionManager.Regions["MainRegion"].Add(view, "ModuleA");
        _regionManager.Regions["MainRegion"].Activate(view);
    }
}

这是添加到该区域的我的视图的视图模型:

public class ViewModel
{
    public DelegateCommand RemoveView { get; set; }

    public ViewModel()
    {
        RemoveView = new DelegateCommand(() =>
            {
                var regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
                var view = regionManager.Regions["MainRegion"].GetView("ModuleA");
                regionManager.Regions["MainRegion"].Deactivate(view);
                regionManager.Regions["MainRegion"].Remove(view);
            });
    }
}

这是视图背后的代码:

public partial class UserControl1 : UserControl
{
    public UserControl1()
    {
        InitializeComponent();

        this.DataContext = new ViewModel();
    }
}

我读到这可能是因为我正在实例化模块中的视图,或者可能是视图中的视图模型?当我使用 Red Gate Memory Profiler 并通过 DelegateCommand 删除视图时,视图和视图模型都被标记为无法进行垃圾收集。我没有正确切割的参考在哪里?

这是来自 Ants 的保留图:https://docs.google.com/file/d/0B4XjO9pUQxBXbGFHS1luNUtyOTg/edit?usp=sharing

这是一个 test solution 显示问题。

另外,我也在CodePlex 上发布了这个问题。

【问题讨论】:

  • 嗨,克里斯。我看了你的项目。看起来您的 ViewModel 实现了 IDisposable。我发现如果不调用 Dispose(),这会对生命周期产生一些奇怪的影响(WPF 与 WinForms 不同,并没有大量使用 IDisposable)。 IDisposable 适用于关闭非托管资源(如数据库连接等),但有更好的方法。试着把它拿出来看看会发生什么。
  • 我应该提到的另一件事是,您应该确保自己是对象生命周期的良好衡量标准。收集不是很确定,我在 WPF 应用程序中发现有时可能需要一段时间才能收集到一个对象。您在 ANT 中的图表看起来不错 - 引用您的视图模型的东西看起来都在某处有 WeakReference 中断,因此它们不应该固定对象。
  • 绑定引用是一个强大的引用,但这应该不是问题,因为您的视图将被收集,然后收集您的 ViewModel。你检查过你的视图的生命周期吗? UserControl1ViewModel 的生命周期应该相同。
  • @Anderson - 感谢您对此进行调查。除了我在下面列出的解决方案之外,我们还需要对未实现 INotifyPropertyChanged 的​​对象的绑定进行一些清理。

标签: wpf memory memory-leaks prism


【解决方案1】:

您的保留图中似乎仍有对它的绑定引用。

阅读并理解以下内容:

A memory leak may occur when you use data binding in Windows Presentation Foundation

我认为这可能是你的问题,但你没有显示你的实际绑定。

【讨论】:

  • 嗨艾伦,是的,它确实似乎来自某处的绑定。我在这里创建了一个测试解决方案,其中我的模块仅绑定(通过 INotifyPropertyChanged)到 1 个文本框,但模块中的视图和视图模型保留在内存中。 docs.google.com/file/d/0B4XjO9pUQxBXVEtJaW8yYWV1SGs/… 。问题是找到什么绑定被搞砸了
  • 在我的示例应用程序中有两个绑定。文本框 Text 的字符串属性和按钮命令的 DelegateCommand。保存这些属性的对象实现 INotifyPropertyChanged。即使我将 TextBox 绑定设置为 OneTime,对象仍保留在内存中。在我们的实际应用程序中,存在大量绑定(MVVM 和 viewmodelfirst 通过 DataTemplates),因此清除所有这些绑定不是一种选择。
【解决方案2】:

终于找到了我的问题的根本原因......

在我们的 Shell.xaml 中,我们将其中一个按钮中的 IsDefault 绑定到 PasswordBox 的 IsKeyboardFocused

<Button Style="{DynamicResource RedSubmitButtonStyle}" IsDefault="{Binding ElementName=passwordBox1, Path=IsKeyboardFocused}" Command="{Binding LoginCommand}" Content="Login" Height="23" HorizontalAlignment="Left" Margin="145,86,0,0" Name="button1" VerticalAlignment="Top" Width="75" />

IsKeyboardFocused,根据 MSDN 是一个依赖属性 - 所以在这方面应该很好。

这与我们在密码框上的附加属性有关,该属性允许我们绑定到输入的密码。即使在我们隐藏了 ChildWindow(来自 WPF 工具包扩展)之后,焦点仍停留在该密码框上。因此,我没有使用 IsDefault,而是在 PasswordBox 中添加了一个 keydown 事件,如果它是 Key.Enter,我将更改聚焦的 UI 控件并将此人登录到程序中。

这是我们的 Shell.xaml 文件的全部内容

<Grid x:Name="MainGrid" core:SharedResourceDictionary.MergedDictionaries="TabControlThemes;MenuThemes;ButtonThemes;DataGridThemes;TreeViewThemes;ComboBoxThemes;ListBoxThemes;GroupBoxThemes;ToggleSwitchThemes">

    <DockPanel>
        <ContentControl x:Name="menuContent" DockPanel.Dock="Top" prism:RegionManager.RegionName="MenuRegion" />
        <ContentControl DockPanel.Dock="Bottom" prism:RegionManager.RegionName="FooterRegion" />
        <ContentControl DockPanel.Dock="Top" prism:RegionManager.RegionName="MainContentRegion" />
    </DockPanel>

    <StackPanel Orientation="Horizontal" Panel.ZIndex="4" HorizontalAlignment="Right" VerticalAlignment="Top">
        <Button Visibility="{Binding IsFullScreenToggleVisible, Converter={StaticResource visConv}}" Command="{Binding ToggleFullScreen}" Height="50" Name="button4" Width="70" HorizontalAlignment="Right" Margin="0,10,10,0" VerticalAlignment="Top">
            <Button.Content>
                <TextBlock FontSize="12" FontWeight="Bold" TextWrapping="Wrap" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" Text=" Toggle Full Screen" />
            </Button.Content>
        </Button>

        <Button Visibility="{Binding IsAppCloseButtonVisible, Converter={StaticResource visConv}}" Command="{Binding ShutdownApplication}" Height="50" Name="button3" Width="50" HorizontalAlignment="Right" Margin="0,10,10,0" VerticalAlignment="Top">
            <Button.Content>
                <Image Source="..\Graphics\close.png" Name="image1" />
            </Button.Content>
        </Button>
    </StackPanel>

    <xctk:ChildWindow Name="loginChildWindow" Panel.ZIndex="3" CloseButtonVisibility="Collapsed" FocusedElement="{Binding ElementName=usernameTextBox}" WindowStartupLocation="Center" WindowState="{Binding IsVisible, Mode=TwoWay, Converter={StaticResource boolConverter}}" IsModal="True" OverlayOpacity="1" Caption="Pioneer Login" Height="164" Width="261">
        <xctk:ChildWindow.OverlayBrush>
            <ImageBrush Stretch="None" Viewport="0,0,46,29" ViewportUnits="Absolute" ImageSource="../Graphics/escheresque.png" TileMode="Tile" />
        </xctk:ChildWindow.OverlayBrush>
        <xctk:BusyIndicator IsBusy="{Binding IsLoginBusy}" BusyContent="Authenticating...">
            <Grid>
                <TextBox GotFocus="usernameTextBox_GotFocus" Text="{Binding Username, UpdateSourceTrigger=PropertyChanged}" Height="23" HorizontalAlignment="Left" Margin="99,20,0,0" Name="usernameTextBox" VerticalAlignment="Top" Width="120">
                    <TextBox.InputBindings>
                        <KeyBinding Key="Enter" Command="{Binding LoginCommand}" />
                    </TextBox.InputBindings>
                </TextBox>
                <Label Content="Username" Height="28" HorizontalAlignment="Left" Margin="28,18,0,0" Name="label1" VerticalAlignment="Top" />
                <Label Content="Password" Height="28" HorizontalAlignment="Left" Margin="29,47,0,0" Name="label2" VerticalAlignment="Top" />
                <PasswordBox attach:PasswordBoxAssistant.BindPassword="True" attach:PasswordBoxAssistant.BoundPassword="{Binding Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="23" HorizontalAlignment="Left" Margin="100,50,0,0" Name="passwordBox1" VerticalAlignment="Top" Width="120" />
                <Button Style="{DynamicResource RedSubmitButtonStyle}" IsDefault="{Binding ElementName=passwordBox1, Path=IsKeyboardFocused}" Command="{Binding LoginCommand}" Content="Login" Height="23" HorizontalAlignment="Left" Margin="145,86,0,0" Name="button1" VerticalAlignment="Top" Width="75" />
                <Button Style="{DynamicResource RedSubmitButtonStyle}" Command="{Binding LazyLoginCommand}" Visibility="{Binding IsDebugMode, Converter={StaticResource visConv}}" Content="Quick Login" Height="23" HorizontalAlignment="Left" Margin="23,87,0,0" Name="button2" VerticalAlignment="Top" Width="89" />
            </Grid>
        </xctk:BusyIndicator>
    </xctk:ChildWindow>

</Grid>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-31
    • 1970-01-01
    • 1970-01-01
    • 2017-03-15
    • 2012-11-30
    • 2012-06-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多