Окно WPF не освобождает свои ресурсы до завершения работы программы - PullRequest
0 голосов
/ 15 марта 2020

Я читал об обработке памяти WPF и следил за каждым 5-ю и 8-ю ловушками утечки памяти, но ничто не помогает мне в моей текущей ситуации.

У меня была проблема с моим программным обеспечением, где WPF не освободит память до тех пор, пока программа не завершится. Если я позволю этому go навсегда, это вызовет исключение OutOfMemoryException, независимо от того, что я делаю. Мне удалось выделить проблему в небольшом примере, чтобы показать, как она не освобождает свою память, хотя я ее больше не использую. Вот как я могу воспроизвести проблему:

Я создал 2 проекта, одну консольную программу и одно приложение WPF. В моем приложении WPF у меня есть MainWindow.xaml, в котором ничего нет:

<Window x:Class="MemoryLeakWpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MemoryLeakWpfApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" Loaded="MainWindow_OnLoaded">
    <Grid>

    </Grid>
</Window>

Я подписываюсь на событие Loaded, которое использую для мгновенного закрытия окна, которое можно увидеть в файле .cs здесь. :

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        Debug.WriteLine("Constructing",GetType().Name);
    }

    ~MainWindow()
    {
        Debug.WriteLine("Deconstructing", GetType().Name);
    }

    private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
    {
        Close();
    }
}

Я также добавил строки отладки в свой конструктор и деконструктор, чтобы я мог отслеживать, когда он создается и удаляется. Затем я создаю класс Controller в приложении WPF, который представляет точку входа в эту библиотеку классов WPF, в которой есть метод для создания и отображения окна:

public class Controller
{
    public void Execute()
    {
        MainWindow window = new MainWindow();
        window.ShowDialog();
        Debug.WriteLine("Constructing", GetType().Name);
    }

    ~Controller()
    {
        Debug.WriteLine("Deconstructing", GetType().Name);
    }
}

Здесь я также добавил строки дорожки отладки. У меня нет App.xaml, так как этот проект WPF настроен как библиотека классов в своих свойствах. Это часть WPF. В консольном проекте я добавил следующий код в свой основной класс:

[STAThread]
static void Main(string[] args)
{
    for (int i = 0; i < 100; i++)
    {
        Controller controller = new Controller();
        Console.WriteLine("Test " + i);
        controller.Execute();
    }

    Console.WriteLine("Pressing enter will close this");
    Console.ReadLine();
    Debug.WriteLine("Party is over, lets leave");
}

Итак, в основном, у меня есть консольный класс, который хочет показать диалог. Он создает контроллер для приложения WPF и вызывает Execute. Контроллер показывает окно, которое сразу же закрывается после завершения загрузки. Затем класс консоли создает новый контроллер для повторного выполнения процесса. Вот что я вижу в своем выводе:

MainWindow: Constructing
Controller: Constructing
MainWindow: Constructing
Controller: Constructing
Controller: Deconstructing
MainWindow: Constructing
Controller: Constructing
Controller: Deconstructing
MainWindow: Constructing
Controller: Constructing
Controller: Deconstructing
MainWindow: Constructing
Controller: Constructing
Controller: Deconstructing

Контроллер строит и деконструирует, а окно - нет. Однако, когда for l oop завершено, и я нажимаю Enter, чтобы позволить программе завершиться, я получаю это:

Party is over, lets leave
MainWindow: Deconstructing
Controller: Deconstructing
MainWindow: Deconstructing
Controller: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing

Внезапно все экземпляры MainWindow теперь деконструируются, но только когда Программа заканчивается, а не когда мы отбрасываем ссылку в for l oop. Это означает, что в нашей программе у нас есть только конечное число раз, когда мы можем открыть окно до возникновения исключения OutOfMemoryException.

Но вопрос на миллион с половиной долларов: Как я могу убедить WPF освободить память во время работы программы, а не при ее закрытии?

1 Ответ

0 голосов
/ 16 марта 2020

Итак, после комментария Питера Дунихо в вопросе, который я поставил перед собой, чтобы проверить, будет ли WindowService для повторного использования windows полезен, он это сделал. Вот очень грубый сервис, который я создал в примере проекта:

public class ViewFactory
{
    private static ViewFactory _instance;
    private MainWindow mainWindow = null;

    private ViewFactory()
    {
        Debug.WriteLine("ViewFactory created");
        mainWindow = new MainWindow();
    }

    public static ViewFactory Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new ViewFactory();
            }

            return _instance;
        }
    }

    public MainWindow GetMainWindow()
    {
        return mainWindow;
    }
}

Теперь с этой системой мне нужно было настроить мой вид, потому что я не могу закрыть свое окно в любое время, так как это высвободит некоторые ресурсы и поэтому я не смог бы повторно использовать в окне. В представлении я должен подписаться на событие закрытия:

<Window x:Class="MemoryLeakWpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MemoryLeakWpfApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" Loaded="MainWindow_OnLoaded" Closing="MainWindow_OnClosing">
    <Grid>

    </Grid>
</Window>

И в файле code-behind обработчик выглядит так:

private void MainWindow_OnClosing(object sender, System.ComponentModel.CancelEventArgs e)
{
    e.Cancel = true;
    Visibility = Visibility.Hidden;
}

Этот обработчик останавливает любую попытку закрытия окно и просто прячет его. Когда ShowDialog вызывается, он покажет его снова. Я часами проверял это на своем программном обеспечении, и память стабильна.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...