求助:多屏托盘窗口关闭时内存泄漏(RAM占用持续上涨)
内存泄漏问题:窗口创建关闭后RAM占用持续上涨
我的程序核心功能是通过托盘为所有屏幕创建窗口,托盘关闭时主程序流程终止。当前遇到内存泄漏问题:每次创建并关闭窗口后内存未被有效清理,导致RAM占用持续上涨。即使手动调用GC,情况也未得到明显改善,不使用GC的话情况会更糟。
TrayIconViewModel 代码
public class TrayIconViewModel : INotifyPropertyChanged { // Commands for user interactions public ICommand CreateWindowCommand { get; } public ICommand ExitCommand { get; } private bool _disposed; public TrayIconViewModel() { // Initializing commands CreateWindowCommand = new RelayCommand(async () => await CreateWindowAsync()); ExitCommand = new RelayCommand(ExitApp); } // Asynchronous method to create a new window private async Task CreateWindowAsync() { await WindowManager.Instance.CreateAndShowWindowAsync(); } // Method to terminate the application private void ExitApp() { // Gracefully shut down the application using the desktop lifetime instance var desktopLifetime = Application.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime; desktopLifetime?.Shutdown(); // Gracefully shut down the application Environment.Exit(0); // Forcibly terminate the process (if needed) } // Implementation of INotifyPropertyChanged to notify UI about property changes public event PropertyChangedEventHandler? PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
ScreenManager 代码
public sealed class ScreenManager { // Singleton instance of ScreenManager private static readonly Lazy<ScreenManager> Lazy = new(() => new ScreenManager()); public static ScreenManager Instance => Lazy.Value; private ScreenManager() { } // Retrieve all screens available without caching as they may change public List<Screen> GetAllScreens() { List<Screen> screens; var tempWindow = new MainWindow(); // Temporary window to access screens try { screens = tempWindow.Screens.All.ToList(); if (screens.Count == 0) { throw new InvalidOperationException("Unable to retrieve screen information."); } } finally { tempWindow.Close(); // Always close the temporary window } return screens; } }
WindowManager 代码
public sealed class WindowManager : IDisposable { // Singleton instance of WindowManager private static readonly Lazy<WindowManager> Lazy = new(() => new WindowManager()); public static WindowManager Instance => Lazy.Value; // Weak reference to the main window to avoid memory leaks private WeakReference<MainWindow>? _mainWindowRef; private bool _disposed; // Structure to track window state (open/closed) private struct WindowState { public bool IsOpen; public bool EventsUnsubscribed; } private WindowState _windowState; // Destructor to ensure proper disposal ~WindowManager() { Dispose(false); } // Method to create and show the main window asynchronously public async Task CreateAndShowWindowAsync() { if (_disposed) throw new ObjectDisposedException(nameof(WindowManager)); if (!_windowState.IsOpen || (_mainWindowRef != null && !_mainWindowRef.TryGetTarget(out _))) { await Dispatcher.UIThread.InvokeAsync(() => { // Get all screens and find the main screen var screens = ScreenManager.Instance.GetAllScreens(); var mainScreen = screens.FirstOrDefault(s => s.Bounds.X <= 0 && s.Bounds.Y <= 0) ?? throw new InvalidOperationException("Main screen not found."); // Combine screen bounds to get the total screen space var combinedBounds = screens.Aggregate(new PixelRect(), (current, screen) => current.Union(screen.Bounds)); // Create and initialize the new window var newWindow = new MainWindow(); InitializeWindow(newWindow, combinedBounds, mainScreen); SubscribeToWindowEvents(newWindow); // Save the window reference and mark it as open _mainWindowRef = new WeakReference<MainWindow>(newWindow); newWindow.Show(); _windowState.IsOpen = true; _windowState.EventsUnsubscribed = false; Console.WriteLine("Window successfully created and displayed."); }); } else if (_mainWindowRef != null && _mainWindowRef.TryGetTarget(out var existingWindow)) { await Dispatcher.UIThread.InvokeAsync(() => existingWindow.Activate()); } } // Method to initialize window properties and position private void InitializeWindow(MainWindow window, PixelRect bounds, Screen mainScreen) { window.Width = bounds.Width / mainScreen.PixelDensity; window.Height = bounds.Height / mainScreen.PixelDensity; window.Position = new PixelPoint(bounds.X, bounds.Y); window.Topmost = true; window.ExtendClientAreaToDecorationsHint = true; window.ExtendClientAreaTitleBarHeightHint = -1; window.CanResize = false; window.ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.NoChrome; } // Subscribe to window events like close and keypress private void SubscribeToWindowEvents(MainWindow window) { window.Closed += OnWindowClosed; window.KeyDown += MainWindow_KeyDown; Console.WriteLine("Subscribed to window events."); } // Unsubscribe from window events to prevent memory leaks private void UnsubscribeFromWindowEvents(MainWindow? window) { if (window != null && !_windowState.EventsUnsubscribed) { window.Closed -= OnWindowClosed; window.KeyDown -= MainWindow_KeyDown; _windowState.EventsUnsubscribed = true; Console.WriteLine("Unsubscribed from window events."); } } // Event handler for window close event private void OnWindowClosed(object? sender, EventArgs e) { CloseWindow(); } // Event handler for key press event (closes window on Escape) private void MainWindow_KeyDown(object? sender, KeyEventArgs e) { if (e.Key == Key.Escape) { CloseWindow(); } } // Method to close the window and clean up resources private void CloseWindow() { if (_windowState.IsOpen && _mainWindowRef != null && _mainWindowRef.TryGetTarget(out var window)) { Dispatcher.UIThread.InvokeAsync(() => { UnsubscribeFromWindowEvents(window); window.Close(); }); _windowState.IsOpen = false; DisposeWindow(); // Force garbage collection to clean up resources GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); Console.WriteLine("Garbage collection performed after window closure."); } } // Method to dispose of the window and release resources private void DisposeWindow() { if (_mainWindowRef != null && _mainWindowRef.TryGetTarget(out var window)) { UnsubscribeFromWindowEvents(window); window.Close(); (window as IDisposable)?.Dispose(); // Explicitly dispose resources _mainWindowRef = null; Console.WriteLine("Window references cleared."); } _windowState.IsOpen = false; } // Dispose pattern implementation to clean up resources private void Dispose(bool disposing) { if (!_disposed) { if (disposing) { DisposeWindow(); } _disposed = true; } } // Public method to dispose resources public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } }
内容的提问来源于stack exchange,提问作者callsign_332
相关产品推荐
相关产品推荐

