Geben Sie Speicher frei, wenn Ihre App in den Hintergrund verschoben wird
In diesem Artikel wird erläutert, wie Sie den Von Der App beim Verschieben in den Hintergrundzustand verwendeten Arbeitsspeicher reduzieren, sodass sie nicht angehalten und möglicherweise beendet werden.
Neue Hintergrundereignisse
Windows 10, Version 1607, führt zwei neue Anwendungslebenszyklusereignisse ein: EnteredBackground und LeavingBackground. Diese Ereignisse informieren Ihre App, wenn sie in den Hintergrund wechselt und den Hintergrund verlässt.
Wenn Ihre App in den Hintergrund wechselt, können sich die vom System erzwungenen Speichereinschränkungen ändern. Verwenden Sie diese Ereignisse, um den aktuellen Speicherverbrauch zu überprüfen und Ressourcen freizugeben, um unter dem Grenzwert zu bleiben, damit Ihre App nicht angehalten und möglicherweise beendet wird, während sie sich im Hintergrund befindet.
Ereignisse zum Steuern der Speicherauslastung Ihrer App
MemoryManager.AppMemoryUsageLimitChanging wird direkt vor dem Grenzwert des gesamten Arbeitsspeichers ausgelöst, den die App verwenden kann. Wenn die App beispielsweise in den Hintergrund wechselt und sich auf der Xbox die Speichergrenze von 1024 MB auf 128 MB ändert.
Dies ist das wichtigste Ereignis, um die Plattform daran zu hindern, die App anzuhalten oder zu beenden.
MemoryManager.AppMemoryUsageIncreased wird ausgelöst, wenn der Speicherverbrauch der App in der AppMemoryUsageLevel-Aufzählung auf einen höheren Wert erhöht wurde. Beispiel: von "Niedrig " bis "Mittel". Die Behandlung dieses Ereignisses ist optional, wird jedoch empfohlen, da die Anwendung weiterhin dafür verantwortlich ist, unter dem Grenzwert zu bleiben.
MemoryManager.AppMemoryUsageDecreased wird ausgelöst, wenn der Speicherverbrauch der App auf einen niedrigeren Wert in der AppMemoryUsageLevel-Aufzählung reduziert wurde. Beispiel: von "Hoch" bis "Niedrig". Die Behandlung dieses Ereignisses ist optional, gibt jedoch an, dass die Anwendung ggf. zusätzlichen Arbeitsspeicher zuweisen kann.
Behandeln des Übergangs zwischen Vordergrund und Hintergrund
Wenn Ihre App vom Vordergrund zum Hintergrund wechselt, wird das EnteredBackground-Ereignis ausgelöst. Wenn Ihre App zum Vordergrund zurückkehrt, wird das LeavingBackground-Ereignis ausgelöst. Sie können Handler für diese Ereignisse registrieren, wenn Ihre App erstellt wird. In der Standardprojektvorlage erfolgt dies im App-Klassenkonstruktor in App.xaml.cs.
Da die Ausführung im Hintergrund die Speicherressourcen verringert, die Ihre App behalten darf, sollten Sie sich auch für die Ereignisse "AppMemoryUsageIncreased" und "AppMemoryUsageLimitChanging" registrieren, die Sie verwenden können, um die aktuelle Speicherauslastung und den aktuellen Grenzwert ihrer App zu überprüfen. Die Handler für diese Ereignisse werden in den folgenden Beispielen gezeigt. Weitere Informationen zum Anwendungslebenszyklus für UWP-Apps finden Sie unter App-Lebenszyklus.
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
// Subscribe to key lifecyle events to know when the app
// transitions to and from foreground and background.
// Leaving the background is an important transition
// because the app may need to restore UI.
this.EnteredBackground += AppEnteredBackground;
this.LeavingBackground += AppLeavingBackground;
// During the transition from foreground to background the
// memory limit allowed for the application changes. The application
// has a short time to respond by bringing its memory usage
// under the new limit.
Windows.System.MemoryManager.AppMemoryUsageLimitChanging += MemoryManager_AppMemoryUsageLimitChanging;
// After an application is backgrounded it is expected to stay
// under a memory target to maintain priority to keep running.
// Subscribe to the event that informs the app of this change.
Windows.System.MemoryManager.AppMemoryUsageIncreased += MemoryManager_AppMemoryUsageIncreased;
}
Wenn das EnteredBackground-Ereignis ausgelöst wird, legen Sie die Nachverfolgungsvariable fest, um anzugeben, dass Sie derzeit im Hintergrund ausgeführt werden. Dies ist nützlich, wenn Sie den Code zum Verringern der Speicherauslastung schreiben.
/// <summary>
/// The application entered the background.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void AppEnteredBackground(object sender, EnteredBackgroundEventArgs e)
{
_isInBackgroundMode = true;
// An application may wish to release views and view data
// here since the UI is no longer visible.
//
// As a performance optimization, here we note instead that
// the app has entered background mode with _isInBackgroundMode and
// defer unloading views until AppMemoryUsageLimitChanging or
// AppMemoryUsageIncreased is raised with an indication that
// the application is under memory pressure.
}
Wenn Ihre App in den Hintergrund wechselt, reduziert das System den Speichergrenzwert für die App, um sicherzustellen, dass die aktuelle Vordergrund-App über ausreichende Ressourcen verfügt, um eine reaktionsfähige Benutzererfahrung zu ermöglichen.
Der AppMemoryUsageLimitChanging-Ereignishandler informiert Ihre App darüber, dass der zugewiesene Arbeitsspeicher reduziert wurde und die neue Grenze in den an den Handler übergebenen Ereignisargumenten bereitstellt. Vergleichen Sie die MemoryManager.AppMemoryUsage-Eigenschaft, die die aktuelle Verwendung Ihrer App bereitstellt, mit der NewLimit-Eigenschaft der Ereignisargumente, die den neuen Grenzwert angibt. Wenn die Speicherauslastung den Grenzwert überschreitet, müssen Sie die Speicherauslastung verringern.
In diesem Beispiel erfolgt dies in der Hilfsmethode ReduceMemoryUsage, die weiter unten in diesem Artikel definiert wird.
/// <summary>
/// Raised when the memory limit for the app is changing, such as when the app
/// enters the background.
/// </summary>
/// <remarks>
/// If the app is using more than the new limit, it must reduce memory within 2 seconds
/// on some platforms in order to avoid being suspended or terminated.
///
/// While some platforms will allow the application
/// to continue running over the limit, reducing usage in the time
/// allotted will enable the best experience across the broadest range of devices.
/// </remarks>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MemoryManager_AppMemoryUsageLimitChanging(object sender, AppMemoryUsageLimitChangingEventArgs e)
{
// If app memory usage is over the limit, reduce usage within 2 seconds
// so that the system does not suspend the app
if (MemoryManager.AppMemoryUsage >= e.NewLimit)
{
ReduceMemoryUsage(e.NewLimit);
}
}
Hinweis
Einige Gerätekonfigurationen ermöglichen es einer Anwendung, weiterhin den neuen Speichergrenzwert zu überschreiten, bis der Ressourcendruck des Systems aufkommt und einige nicht. Auf Xbox werden Apps insbesondere angehalten oder beendet, wenn sie den Speicher nicht innerhalb von 2 Sekunden unter den neuen Grenzwerten reduzieren. Dies bedeutet, dass Sie die beste Erfahrung über die breiteste Palette von Geräten hinweg bereitstellen können, indem Sie dieses Ereignis verwenden, um die Ressourcennutzung innerhalb von 2 Sekunden nach dem Auslösen des Ereignisses unter dem Grenzwert zu reduzieren.
Es ist möglich, dass die Speicherauslastung Ihrer App derzeit unter dem Speicherlimit für Hintergrund-Apps liegt, wenn sie zum ersten Mal in den Hintergrund wechselt, aber sie kann den Speicherverbrauch im Laufe der Zeit erhöhen und beginnen, den Grenzwert zu erreichen. Der Handler der AppMemoryUsageIncreased bietet die Möglichkeit, die aktuelle Nutzung zu überprüfen, wenn sie zunimmt und ggf. Arbeitsspeicher freigibt.
Überprüfen Sie, ob appMemoryUsageLevel hoch oder überlimit ist, und verringern Sie ggf. die Speicherauslastung. In diesem Beispiel wird dies von der Hilfsmethode ReduceMemoryUsage behandelt. Sie können auch das AppMemoryUsageDecreased-Ereignis abonnieren, überprüfen, ob Ihre App unter dem Grenzwert liegt, und wenn ja, wissen Sie, dass Sie zusätzliche Ressourcen zuordnen können.
/// <summary>
/// Handle system notifications that the app has increased its
/// memory usage level compared to its current target.
/// </summary>
/// <remarks>
/// The app may have increased its usage or the app may have moved
/// to the background and the system lowered the target for the app
/// In either case, if the application wants to maintain its priority
/// to avoid being suspended before other apps, it may need to reduce
/// its memory usage.
///
/// This is not a replacement for handling AppMemoryUsageLimitChanging
/// which is critical to ensure the app immediately gets below the new
/// limit. However, once the app is allowed to continue running and
/// policy is applied, some apps may wish to continue monitoring
/// usage to ensure they remain below the limit.
/// </remarks>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MemoryManager_AppMemoryUsageIncreased(object sender, object e)
{
// Obtain the current usage level
var level = MemoryManager.AppMemoryUsageLevel;
// Check the usage level to determine whether reducing memory is necessary.
// Memory usage may have been fine when initially entering the background but
// the app may have increased its memory usage since then and will need to trim back.
if (level == AppMemoryUsageLevel.OverLimit || level == AppMemoryUsageLevel.High)
{
ReduceMemoryUsage(MemoryManager.AppMemoryUsageLimit);
}
}
"ReduceMemoryUsage" ist eine Hilfsmethode, die Sie implementieren können, um Arbeitsspeicher freizugeben, wenn die App den Nutzungsgrenzwert überschreitet, während sie im Hintergrund ausgeführt wird. Wie Sie Arbeitsspeicher freigeben, hängt von den Spezifischen Ihrer App ab, aber eine empfohlene Möglichkeit zum Freigeben von Arbeitsspeicher besteht darin, die Benutzeroberfläche und die anderen Ressourcen zu löschen, die Ihrer App-Ansicht zugeordnet sind. Stellen Sie hierzu sicher, dass Sie im Hintergrundzustand ausgeführt werden, und legen Sie dann die Inhaltseigenschaft des Fensters Ihrer App auf null
die Registrierung der UI-Ereignishandler fest und heben Sie die Registrierung der UI-Ereignishandler auf, und entfernen Sie alle anderen Verweise, die Sie möglicherweise auf der Seite haben. Wenn Sie die Registrierung der UI-Ereignishandler nicht aufheben und alle anderen Verweise löschen, die Sie möglicherweise auf der Seite haben, wird verhindert, dass die Seitenressourcen freigegeben werden. Rufen Sie dann GC auf . Sammeln Sie , um den freigegebenen Speicher sofort freizugeben. In der Regel erzwingen Sie keine Garbage Collection, da das System sie für Sie übernimmt. In diesem speziellen Fall verringern wir die Menge an Arbeitsspeicher, der für diese Anwendung in den Hintergrund geladen wird, um die Wahrscheinlichkeit zu verringern, dass das System feststellt, dass die App beendet werden soll, um Arbeitsspeicher freizugeben.
/// <summary>
/// Reduces application memory usage.
/// </summary>
/// <remarks>
/// When the app enters the background, receives a memory limit changing
/// event, or receives a memory usage increased event, it can
/// can optionally unload cached data or even its view content in
/// order to reduce memory usage and the chance of being suspended.
///
/// This must be called from multiple event handlers because an application may already
/// be in a high memory usage state when entering the background, or it
/// may be in a low memory usage state with no need to unload resources yet
/// and only enter a higher state later.
/// </remarks>
public void ReduceMemoryUsage(ulong limit)
{
// If the app has caches or other memory it can free, it should do so now.
// << App can release memory here >>
// Additionally, if the application is currently
// in background mode and still has a view with content
// then the view can be released to save memory and
// can be recreated again later when leaving the background.
if (isInBackgroundMode && Window.Current.Content != null)
{
// Some apps may wish to use this helper to explicitly disconnect
// child references.
// VisualTreeHelper.DisconnectChildrenRecursive(Window.Current.Content);
// Clear the view content. Note that views should rely on
// events like Page.Unloaded to further release resources.
// Release event handlers in views since references can
// prevent objects from being collected.
Window.Current.Content = null;
}
// Run the GC to collect released resources.
GC.Collect();
}
Wenn der Fensterinhalt erfasst wird, beginnt jeder Frame mit dem Trennen des Vorgangs. Wenn seiten in der visuellen Objektstruktur unter dem Fensterinhalt vorhanden sind, beginnt dies mit dem Auslösen des ungeladenen Ereignisses. Seiten können nicht vollständig aus dem Speicher gelöscht werden, es sei denn, alle Verweise auf sie werden entfernt. Führen Sie im Unloaded-Rückruf die folgenden Schritte aus, um sicherzustellen, dass der Arbeitsspeicher schnell freigegeben wird:
- Löschen und festlegen Sie alle großen Datenstrukturen in Ihrer Seite auf
null
. - Heben Sie die Registrierung aller Ereignishandler auf, die Über Rückrufmethoden innerhalb der Seite verfügen. Registrieren Sie diese Rückrufe während des Loaded-Ereignishandlers für die Seite. Das Loaded-Ereignis wird ausgelöst, wenn die Benutzeroberfläche wiederhergestellt wurde und die Seite der visuellen Objektstruktur hinzugefügt wurde.
- Rufen Sie
GC.Collect
am Ende des ungeladenen Rückrufs auf, um schnell eine der großen Datenstrukturen zu sammeln, dienull
Sie gerade festgelegt haben. Auch hier erzwingen Sie in der Regel keine Garbage Collection, da das System sie für Sie übernimmt. In diesem speziellen Fall verringern wir die Menge an Arbeitsspeicher, der für diese Anwendung in den Hintergrund geladen wird, um die Wahrscheinlichkeit zu verringern, dass das System feststellt, dass die App beendet werden soll, um Arbeitsspeicher freizugeben.
private void MainPage_Unloaded(object sender, RoutedEventArgs e)
{
// << free large data sructures and set them to null, here >>
// Disconnect event handlers for this page so that the garbage
// collector can free memory associated with the page
Window.Current.Activated -= Current_Activated;
GC.Collect();
}
Legen Sie im LeavingBackground-Ereignishandler die Nachverfolgungsvariable (isInBackgroundMode
) fest, um anzugeben, dass Ihre App nicht mehr im Hintergrund ausgeführt wird. Überprüfen Sie als Nächstes, ob der Inhalt des aktuellen Fensters lautet null
– was der Vorgang sein wird, wenn Sie die App-Ansichten verworfen haben, um Speicher zu löschen, während Sie im Hintergrund ausgeführt wurden. Wenn der Fensterinhalt lautet null
, erstellen Sie die App-Ansicht neu. In diesem Beispiel wird der Fensterinhalt in der Hilfsmethode CreateRootFrame erstellt.
/// <summary>
/// The application is leaving the background.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void AppLeavingBackground(object sender, LeavingBackgroundEventArgs e)
{
// Mark the transition out of the background state
_isInBackgroundMode = false;
// Restore view content if it was previously unloaded
if (Window.Current.Content == null)
{
CreateRootFrame(ApplicationExecutionState.Running, string.Empty);
}
}
Die CreateRootFrame-Hilfsmethode erstellt den Ansichtsinhalt für Ihre App neu. Der Code in dieser Methode ist nahezu identisch mit dem OnLaunched-Handlercode, der in der Standardprojektvorlage bereitgestellt wird. Der einzige Unterschied besteht darin, dass der Launch-Handler den vorherigen Ausführungszustand aus der PreviousExecutionState-Eigenschaft der LaunchActivatedEventArgs bestimmt und die CreateRootFrame-Methode einfach den vorherigen Ausführungszustand abruft, der als Argument übergeben wird. Um doppelten Code zu minimieren, können Sie den standardmäßigen Startereignishandlercode so umgestalten, dass CreateRootFrame aufgerufen wird.
void CreateRootFrame(ApplicationExecutionState previousExecutionState, string arguments)
{
Frame rootFrame = Window.Current.Content as Frame;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
// Set the default language
rootFrame.Language = Windows.Globalization.ApplicationLanguages.Languages[0];
rootFrame.NavigationFailed += OnNavigationFailed;
if (previousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
}
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
if (rootFrame.Content == null)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
rootFrame.Navigate(typeof(MainPage), arguments);
}
}
Richtlinien
Wechseln vom Vordergrund zum Hintergrund
Wenn eine App vom Vordergrund zum Hintergrund wechselt, funktioniert das System im Auftrag der App, um Ressourcen freizugeben, die im Hintergrund nicht benötigt werden. Beispielsweise leeren die UI-Frameworks zwischengespeicherte Texturen, und das Videosubsystem gibt Speicher frei, der im Auftrag der App zugewiesen wird. Eine App muss die Speicherauslastung jedoch weiterhin sorgfältig überwachen, um zu vermeiden, dass sie vom System angehalten oder beendet wird.
Wenn eine App vom Vordergrund in den Hintergrund wechselt, wird zuerst ein EnteredBackground-Ereignis und dann ein AppMemoryUsageLimitChanging-Ereignis abgerufen.
- Verwenden Sie das EnteredBackground-Ereignis , um UI-Ressourcen freizugeben, die Ihre App nicht benötigt, während sie im Hintergrund ausgeführt wird. So könnten Sie beispielsweise das CoverArt-Bild für einen Song freigeben.
- Verwenden Sie das AppMemoryUsageLimitChanging-Ereignis , um sicherzustellen, dass Ihre App weniger Arbeitsspeicher als die neue Hintergrundgrenze verwendet. Stellen Sie sicher, dass Sie Ressourcen freigeben, wenn dies nicht der Fall ist. Wenn Sie dies nicht tun, wird Ihre App möglicherweise entsprechend der gerätespezifischen Richtlinie angehalten oder beendet.
- Rufen Sie den Garbage Collector manuell auf, wenn Ihre App den neuen Speichergrenzwert überschreitet, wenn das AppMemoryUsageLimitChanging-Ereignis ausgelöst wird.
- Verwenden Sie das AppMemoryUsageIncreased-Ereignis , um die Speicherauslastung Ihrer App weiterhin zu überwachen, während sie im Hintergrund ausgeführt wird, wenn sie sich ändern wird. Wenn "AppMemoryUsageLevel" hoch oder überlimit ist, stellen Sie sicher, dass Sie Ressourcen freigeben.
- Erwägen Sie das Freigeben von UI-Ressourcen im AppMemoryUsageLimitChanging-Ereignishandler anstelle des EnteredBackground-Handlers als Leistungsoptimierung. Verwenden Sie einen booleschen Wert, der in den EnteredBackground/LeavingBackground-Ereignishandlern festgelegt ist, um nachzuverfolgen, ob sich die App im Hintergrund oder Im Vordergrund befindet. Wenn "AppMemoryUsageLimitChanging" den Grenzwert überschreitet und sich die App im Hintergrund befindet (basierend auf dem booleschen Wert), können Sie UI-Ressourcen freigeben.
- Führen Sie keine Vorgänge mit langer Ausführung im EnteredBackground-Ereignis aus, da der Übergang zwischen Anwendungen für den Benutzer langsam erscheint.
Wechseln vom Hintergrund zum Vordergrund
Wenn eine App vom Hintergrund in den Vordergrund wechselt, ruft die App zuerst ein AppMemoryUsageLimitChanging-Ereignis und dann ein LeavingBackground-Ereignis ab.
- Verwenden Sie das LeavingBackground-Ereignis , um UI-Ressourcen neu zu erstellen, die Ihre App beim Wechseln in den Hintergrund verworfen hat.
Zugehörige Themen
- Beispiel für die Wiedergabe von Hintergrundmedien – zeigt, wie Sie Arbeitsspeicher freigeben, wenn Ihre App in den Hintergrundzustand wechselt.
- Diagnosetools – Verwenden Sie die Diagnosetools, um Garbage Collection-Ereignisse zu beobachten und zu überprüfen, ob Ihre App Arbeitsspeicher wie erwartet freigibt.