Haben Sie jemals eine Flash-Anwendung verwendet und eine Verzögerung darin festgestellt? Sie wissen immer noch nicht, warum dieses coole Flash-Spiel langsam auf Ihrem Computer läuft? Wenn Sie mehr über eine mögliche Ursache erfahren möchten, ist dieser Artikel für Sie bestimmt.
Wir haben diesen großartigen Autor dank FlashGameLicense.com, dem Ort zum Kaufen und Verkaufen von Flash-Spielen, gefunden!
Erneut veröffentlichtes TutorialAlle paar Wochen besuchen wir einige der Lieblingsbeiträge unserer Leser aus der gesamten Geschichte der Website. Dieses Tutorial wurde erstmals im Juni 2010 veröffentlicht.
Werfen wir einen Blick auf das Endergebnis, auf das wir hinarbeiten:
Bevor wir uns mit dem eigentlichen Thema befassen, müssen Sie zunächst ein wenig wissen, wie Instantiating und Referenzieren in AS3 funktionieren. Wenn Sie bereits darüber gelesen haben, empfehle ich trotzdem, diesen kleinen Schritt zu lesen. Auf diese Weise wird das gesamte Wissen in Ihrem Kopf frisch sein, und Sie werden keine Probleme haben, den Rest dieses Schnelltipps zu lesen!
Die Erstellung und Referenzierung von Instanzen in AS3 ist anders, als die meisten Leute denken. Die Instantiierung (oder "Erstellung") von etwas geschieht nur, wenn der Code zum Erstellen eines Objekts auffordert. Normalerweise geschieht dies durch das Schlüsselwort "new", es ist jedoch auch vorhanden, wenn Sie ein wörtliche Syntax oder definieren Sie Parameter für Funktionen, zum Beispiel. Beispiele dafür sind unten gezeigt:
// Instantiierung durch das Schlüsselwort "new" new Object (); neues Array (); new int (); neuer String (); new Boolean (); neues Datum(); // Instantiation durch wörtliche Syntax ; []; 5 "Hallo Welt!" true // Instantiation durch Funktionsparameter private Funktion tutExample (Parameter1: int, Parameter2: Boolean): void
Nachdem ein Objekt erstellt wurde, bleibt es alleine, bis es von einem Objekt referenziert wird. Dazu erstellen Sie im Allgemeinen eine Variable und übergeben den Wert des Objekts an die Variable, sodass es weiß, welches Objekt es aktuell enthält. Allerdings (und dies ist der Teil, den die meisten nicht kennen): Wenn Sie den Wert einer Variablen an eine andere Variable übergeben, erstellen Sie kein neues Objekt. Sie erstellen stattdessen eine weitere Verknüpfung zu dem Objekt, das beide Variablen jetzt enthalten! Siehe das Bild unten zur Verdeutlichung:
Das Bild nimmt beides an Variable 1 und Variable 2 kann den Smiley halten (d. h. sie können denselben Typ haben). Nur auf der linken Seite Variable 1 existiert. Wenn wir jedoch erstellen und festlegen Variable 2 auf den gleichen Wert von Variable 1, Wir schaffen keine Verbindung zwischen Variable 1 und Variable 2 (oben rechts im Bild), stattdessen erstellen wir eine Verbindung zwischen dem Smiley und Variable 2 (unterer rechter Teil des Bildes).
Mit diesem Wissen können wir zum Müllsammler springen.
Es ist offensichtlich, dass jede Anwendung eine bestimmte Menge Speicher benötigt, um ausgeführt zu werden, da sie Variablen benötigt, um Werte zu speichern und sie zu verwenden. Unklar ist, wie die Anwendung die Objekte verwaltet, die nicht mehr benötigt werden. Recycelt sie? Löscht es sie? Bleibt das Objekt im Speicher, bis die Anwendung geschlossen wird? Alle drei Optionen können vorkommen, aber hier werden wir speziell über die zweite und dritte sprechen.
Stellen Sie sich eine Situation vor, in der eine Anwendung bei der Initialisierung viele Objekte erstellt, aber nach Ablauf dieses Zeitraums bleiben mehr als die Hälfte der erstellten Objekte ungenutzt. Was würde passieren, wenn sie in Erinnerung bleiben würden? Sie würden sicherlich viel Platz in Anspruch nehmen und so das verursachen, was die Leute nennen Verzögerung, was eine spürbare Verlangsamung in der Anwendung ist. Die meisten Benutzer mögen das nicht, deshalb müssen wir es vermeiden. Wie können wir kodieren, damit die Anwendung effizienter läuft? Die Antwort ist in der Müllsammler.
Der Garbage Collector ist eine Form der Speicherverwaltung. Ziel ist es, alle Objekte zu entfernen, die nicht verwendet werden und Platz im Speicher des Systems belegen. Auf diese Weise kann die Anwendung mit minimaler Speichernutzung ausgeführt werden. Mal sehen, wie es funktioniert:
Wenn Ihre Anwendung gestartet wird, werden Sie vom System nach einer Menge Speicher gefragt, die von der Anwendung verwendet wird. Die Anwendung startet und füllt dann diesen Speicher mit allen erforderlichen Informationen. Jedes Objekt, das Sie erstellen, wird in dieses Objekt aufgenommen. Wenn jedoch die Speichernutzung nahe an den ursprünglich angeforderten Speicher herankommt, wird der Garbage Collector ausgeführt und sucht nach Objekten, die nicht zum Leeren von Speicherplatz verwendet werden. In manchen Fällen führt dies zu Verzögerungen in der Anwendung, da die Objektsuche sehr viel Aufwand verursacht.
Im Bild sehen Sie die Gedächtnisspitzen (grün eingekreist). Die Spitzen und der plötzliche Abfall werden durch den Garbage Collector verursacht, der dann wirkt, wenn die Anwendung die angeforderte Speicherbelegung (die rote Linie) erreicht hat und alle unnötigen Objekte entfernt.
Nun, da wir wissen, was der Garbage Collector für uns tun kann, ist es an der Zeit zu lernen, wie man codiert, um alle Vorteile daraus zu ziehen. Zunächst müssen wir wissen, wie der Garbage Collector funktioniert, und zwar aus praktischer Sicht. Im Code können Objekte für die Garbage Collection verwendet werden, wenn sie nicht mehr erreichbar sind. Wenn auf ein Objekt nicht zugegriffen werden kann, erkennt der Code, dass es nicht mehr verwendet wird. Daher muss es gesammelt werden.
Actionscript 3 prüft die Erreichbarkeit durch Müllsammelwurzeln. In dem Moment, auf den ein Objekt nicht über eine Garbage Collection-Wurzel zugegriffen werden kann, ist es für die Sammlung verfügbar. Unten sehen Sie eine Liste der wichtigsten Müllsammelwurzeln:
Um zu verstehen, wie Objekte vom Garbage Collector behandelt werden, müssen wir kodieren und untersuchen, was in der Beispieldatei passiert. Ich werde das AS3-Projekt von FlashDevelop und den Compiler von Flex verwenden, aber ich gehe davon aus, dass Sie es mit jeder gewünschten IDE machen können, da wir nicht bestimmte Dinge verwenden werden, die nur in FlashDevelop existieren. Ich habe eine einfache Datei mit einer Schaltfläche und einer Textstruktur erstellt. Da dies nicht das Ziel in diesem schnellen Tipp ist, werde ich es schnell erklären: Wenn eine Schaltfläche angeklickt wird, wird eine Funktion ausgelöst. Wir möchten jederzeit etwas Text auf dem Bildschirm anzeigen, Sie rufen eine Funktion mit dem Text auf und es wird angezeigt. Es gibt auch ein weiteres Textfeld, um eine Beschreibung für Schaltflächen anzuzeigen.
Das Ziel unserer Beispieldatei besteht darin, Objekte zu erstellen, sie zu löschen und zu untersuchen, was nach dem Löschen mit ihnen geschieht. Wir benötigen einen Weg, um zu wissen, ob das Objekt lebendig ist oder nicht, also fügen wir jedem Objekt einen ENTER_FRAME-Listener hinzu und lassen sie etwas Text mit der Zeit anzeigen, zu der sie lebten. Lassen Sie uns also das erste Objekt codieren!
Ich habe ein lustiges Smiley-Image für die Objekte erstellt, zu Ehren des großartigen Avoider-Tutorials von Michael James Williams, das auch Smiley-Images verwendet. Jedes Objekt hat eine Nummer auf dem Kopf, damit wir es identifizieren können. Ich habe auch das erste Objekt benannt TheObject1, und das zweite Objekt TheObject2, so wird es leicht zu unterscheiden sein. Gehen wir zum Code:
private var _theObject1: TheObject1; private Funktion newObjectSimple1 (e: MouseEvent): void // Wenn bereits ein Objekt erstellt wurde, tun Sie nichts, wenn (_theObject1) return; // Erstellen Sie das neue Objekt, setzen Sie es auf die Position, in der es sich befinden soll, und fügen Sie es zur Anzeigeliste hinzu, damit wir sehen können, dass es erstellt wurde. _TheObject1 = new TheObject1 (); _theObject1.x = 320; _theObject1.y = 280; _theObject1.addEventListener (Event.ENTER_FRAME, changeTextField1); addChild (_theObject1);
Das zweite Objekt sieht fast gleich aus. Hier ist es:
private var _theObject2: TheObject2; private Funktion newObjectSimple2 (e: MouseEvent): void // Wenn bereits ein Objekt erstellt wurde, müssen Sie nichts tun, wenn (_theObject2) return; // Erstellen Sie das neue Objekt, setzen Sie es auf die Position, an der es sich befinden soll, und fügen Sie der Anzeigeliste hinzu, damit wir sehen können, dass es erstellt wurde. _TheObject2 = new TheObject2 (); _theObject2.x = 400; _theObject2.y = 280; _theObject2.addEventListener (Event.ENTER_FRAME, changeTextField2); addChild (_theObject2);
Im Code, newObjectSimple1 () und newObjectSimple2 () sind Funktionen, die ausgelöst werden, wenn auf die entsprechende Schaltfläche geklickt wird. Diese Funktionen erstellen einfach ein Objekt und fügen es im Anzeigebildschirm hinzu, sodass wir wissen, dass es erstellt wurde. Außerdem wird ein erstellt ENTER_FRAME Ereignis-Listener in jedem Objekt, wodurch jede Sekunde eine Nachricht angezeigt wird, solange sie aktiv ist. Hier sind die Funktionen:
private Funktion changeTextField1 (e: Event): void // Unser Beispiel läuft mit 30FPS. Fügen wir also bei jedem Frame 1/30 1/30 hinzu. _objectCount1 + = 0,034; // Überprüft, ob _objectCount1 noch eine Sekunde überschritten wurde if (int (_objectCount1)> _secondCount1) // Zeigt einen Text im Bildschirm an. DisplayText ("Object 1 is alive…" + int (_objectCount1)); _secondCount1 = int (_objectCount1);
private Funktion changeTextField2 (e: Event): void // Unser Beispiel läuft mit 30FPS. Fügen wir also bei jedem Frame 1/30 1/30 hinzu. _objectCount2 + = 0,034; // Überprüft, ob _objectCount2 noch eine Sekunde überschritten wurde if (int (_objectCount2)> _secondCount2) // Zeigt einen Text im Bildschirm displayText an ("Object 2 is alive…" + int (_objectCount2)); _secondCount2 = int (_objectCount2);
Diese Funktionen zeigen einfach eine Nachricht auf dem Bildschirm an, während die Objekte lebendig sind. Hier ist die SWF-Datei mit dem aktuellen Beispiel:
Nun, da wir uns mit der Erstellung von Objekten befasst haben, versuchen wir etwas: Haben Sie sich jemals gefragt, was passiert, wenn Sie ein Objekt tatsächlich löschen (alle Referenzen entfernen)? Bekommt es Müll gesammelt? Das werden wir jetzt testen. Wir werden zwei Löschschaltflächen erstellen, eine für jedes Objekt. Lass uns den Code für sie machen:
private Funktion deleteObject1 (e: MouseEvent): void // Prüfen Sie, ob _theObject1 wirklich existiert, bevor Sie es aus der Anzeigeliste entfernen, wenn (_theObject1 && enthält (_theObject1)) removeChild (_theObject1); // Alle Verweise auf das Objekt entfernen (dies ist die einzige Referenz) _theObject1 = null; // Zeigt einen Text auf dem Bildschirm an displayText ("Gelöschtes Objekt 1 erfolgreich!");
private Funktion deleteObject2 (e: MouseEvent): void // Prüfen Sie, ob _theObject2 wirklich existiert, bevor Sie es aus der Anzeigeliste entfernen, wenn (_theObject1 && enthält (_theObject2)) removeChild (_theObject2); // Alle Verweise auf das Objekt entfernen (dies ist die einzige Referenz) _theObject2 = null; // Zeigt einen Text auf dem Bildschirm an. DisplayText ("Gelöschtes Objekt 2 erfolgreich!");
Schauen wir uns jetzt die SWF an. Was denkst du wird passieren?
Wie du siehst. Wenn Sie auf "Objekt1 erstellen" und dann auf "Objekt1 löschen" klicken, passiert wirklich nichts! Wir können feststellen, dass der Code ausgeführt wird, da der Text auf dem Bildschirm angezeigt wird. Warum wird das Objekt jedoch nicht gelöscht? Das Objekt ist immer noch da, weil es nicht wirklich entfernt wurde. Als wir alle Verweise darauf gelöscht haben, haben wir dem Code mitgeteilt, dass er für die Garbage Collection geeignet ist. Der Garbage Collection wird jedoch nicht ausgeführt. Beachten Sie, dass der Garbage Collector nur dann ausgeführt wird, wenn sich die aktuelle Speichernutzung dem angeforderten Speicher nähert, wenn die Anwendung ausgeführt wird. Es macht Sinn, aber wie wollen wir das testen??
Ich werde sicherlich keinen Code schreiben, um unsere Anwendung mit nutzlosen Objekten zu füllen, bis der Speicherbedarf zu groß wird. Stattdessen verwenden wir eine von Adobe derzeit nicht unterstützte Funktion. Dies wird im Artikel von Grant Skinner beschrieben, der den Garbage Collector zur Ausführung zwingt. Auf diese Weise können wir diese einfache Methode auslösen und sehen, was passiert, wenn sie ausgeführt wird. Ab jetzt werde ich der Einfachheit halber auch Garbage Collector als GC bezeichnen. Hier ist die Funktion:
private Funktion forceGC (e: MouseEvent): void try new LocalConnection (). connect ('foo'); new LocalConnection (). connect ('foo'); catch (e: *) // Zeigt einen Text auf dem Bildschirm displayText an ("----- Garbage Collection ausgelöst -----");
Diese einfache Funktion, die nur zwei LocalConnection () -Objekte erstellt, erzwingt die Ausführung des GCs. Wir werden sie also aufrufen, wenn dies gewünscht wird. Ich empfehle nicht, diese Funktion in einer ernsthaften Anwendung zu verwenden. Wenn Sie dies für einen Test tun, gibt es keine echten Probleme, aber wenn es sich um eine Anwendung handelt, die an Benutzer verteilt wird, ist dies keine gute Funktion, da dies negative Auswirkungen haben kann.
Was ich für Fälle wie diesen empfehle, ist, dass Sie den GC in seinem eigenen Tempo laufen lassen. Versuchen Sie nicht, es zu erzwingen. Konzentrieren Sie sich stattdessen auf effizientes Codieren, damit keine Speicherprobleme auftreten (wir werden dies in Schritt 6 behandeln). Schauen wir uns noch einmal unsere Beispiel-SWF-Datei an und klicken Sie auf die Schaltfläche "Müll sammeln", nachdem Sie ein Objekt erstellt und gelöscht haben.
Hast du die Datei getestet? Es funktionierte! Sie können das jetzt sehen, nachdem Sie ein Objekt gelöscht und den GC ausgelöst haben, entfernt es das Objekt! Beachten Sie, dass nichts passiert, wenn Sie das Objekt nicht löschen und den GC aufrufen, da im Code immer noch ein Verweis auf dieses Objekt enthalten ist. Was ist, wenn wir versuchen, zwei Verweise auf ein Objekt zu behalten und einen von ihnen zu entfernen?
Nachdem wir nun bewiesen haben, dass der GC genau so funktioniert, wie wir wollten, versuchen wir etwas anderes: Verknüpfen Sie einen anderen Verweis auf ein Objekt (Object1) und entfernen Sie das Original. Zuerst müssen wir eine Funktion erstellen, um einen Verweis auf unser Objekt zu verknüpfen. Machen wir das:
private Funktion saveObject1 (e: MouseEvent): void // _onSave ist ein Boolean, um zu prüfen, ob die Referenz verknüpft werden soll, wenn (_onSave) // Wenn kein zu speicherndes Objekt vorhanden ist, ist nichts zu tun, wenn (! _theObject1) // Zeigt einen Text auf dem Bildschirm an. DisplayText ("Es ist kein Objekt 1 zum Speichern vorhanden!"); Rückkehr; // Eine neue Variable, die eine weitere Referenz auf Object1 enthält _theSavedObject = _theObject1; // Zeigt einen Text auf dem Bildschirm an. DisplayText ("Objekt 1 erfolgreich gespeichert!"); // Wenn diese Funktion das nächste Mal ausgeführt wird, heben Sie die Verknüpfung auf, da wir gerade _onSave = false verknüpft haben. else // Den Verweis darauf entfernen _theSavedObject = null; // Zeigt einen Text auf dem Bildschirm an. DisplayText ("Nicht gespeichertes Objekt 1 erfolgreich!"); // Wenn diese Funktion das nächste Mal ausgeführt wird, verknüpfen Sie sie, da wir gerade die Verknüpfung mit _onSave = true aufgehoben haben.
Wenn wir unseren SWF jetzt testen, werden wir feststellen, dass nichts passiert, wenn wir Object1 erstellen, dann speichern, löschen und den GC zum Ausführen zwingen. Das liegt daran, dass jetzt, auch wenn wir die "ursprüngliche" Verknüpfung zu dem Objekt entfernt haben, noch ein weiterer Verweis darauf vorhanden ist, der verhindert, dass eine Müllsammlung möglich ist. Dies ist im Grunde alles, was Sie über den Garbage Collector wissen müssen. Es ist schließlich kein Geheimnis. Aber wie können wir das in unserer aktuellen Umgebung anwenden? Wie können wir dieses Wissen nutzen, um zu verhindern, dass unsere Anwendung langsam läuft? Dies wird uns in Schritt 6 gezeigt: Wie können wir dies in realen Beispielen anwenden?.
Nun zum besten Teil: Damit Ihr Code effizient mit dem GC funktioniert! Dieser Schritt enthält nützliche Informationen, die Sie für Ihr ganzes Leben aufbewahren sollten - speichern Sie sie richtig! Zunächst möchte ich eine neue Methode zum Erstellen Ihrer Objekte in Ihrer Anwendung einführen. Es ist eine einfache, aber effektive Möglichkeit, mit dem GC zusammenzuarbeiten. Auf diese Weise werden zwei einfache Klassen eingeführt, die auf andere erweitert werden können, sobald Sie verstanden haben, was sie tun.
Die Idee dieser Methode besteht darin, eine Funktion namens "destroy ()" für jedes von Ihnen erstellte Objekt zu implementieren und jedes Mal aufzurufen, wenn Sie mit einem Objekt fertig sind. Die Funktion enthält den gesamten Code, der zum Entfernen aller Verweise auf und vom Objekt erforderlich ist (mit Ausnahme der zum Aufruf der Funktion verwendeten Referenz). So stellen Sie sicher, dass das Objekt Ihre Anwendung vollständig isoliert lässt und vom GC leicht erkannt wird. Der Grund dafür wird im nächsten Schritt erläutert. Schauen wir uns den allgemeinen Code für die Funktion an:
// Dies in jedem Objekt erstellen, das Sie mit der öffentlichen Funktion destroy () verwenden: void // Ereignis-Listener entfernen // Alles in der Anzeigeliste entfernen // Die Verweise auf andere Objekte löschen, damit sie vollständig isoliert werden //… // Wenn Sie das Objekt entfernen möchten, führen Sie folgende Schritte aus: theObject.destroy (); // Und dann den letzten Verweis darauf nullen theObject = null;
In dieser Funktion müssen Sie alles vom Objekt löschen, damit es in der Anwendung isoliert bleibt. Danach ist es für den GC einfacher, das Objekt zu lokalisieren und zu entfernen. Betrachten wir nun einige Situationen, in denen die meisten Speicherfehler auftreten:
Die Arbeit mit dem GC ist zwar großartig, aber nicht perfekt. Sie müssen darauf achten, was Sie tun, da sonst schlechte Dinge mit Ihrer Anwendung passieren können. Ich möchte ein Problem aufzeigen, das auftreten kann, wenn Sie nicht alle erforderlichen Schritte ausführen, damit Ihr Code ordnungsgemäß mit dem GC funktioniert.
Wenn Sie nicht alle Verweise auf und von einem Objekt löschen, kann dieses Problem möglicherweise auftreten, insbesondere wenn Sie in Ihrer Anwendung viele Objekte miteinander verknüpfen. In manchen Fällen reicht eine einzige Referenz, um dies zu erreichen: Alle Ihre Objekte bilden eine Insel von Referenzen, in der alle Objekte mit anderen verbunden sind, sodass der GC sie nicht entfernen kann.
Wenn der GC ausgeführt wird, führt er zwei einfache Aufgaben aus, um zu prüfen, ob Objekte gelöscht werden sollen. Eine dieser Aufgaben zählt, wie viele Referenzen jedes Objekt hat. Alle Objekte mit 0 Referenzen werden gleichzeitig gesammelt. Die andere Aufgabe besteht darin, zu prüfen, ob eine kleine Anzahl von Objekten vorhanden ist, die miteinander verbunden sind, aber nicht darauf zugegriffen werden können, wodurch Speicherplatz verschwendet wird. Überprüfen Sie das Bild:
Wie Sie sehen, können die grünen Objekte nicht erreicht werden. Die Referenzzählung ist jedoch 1. Der GC führt die zweite Aufgabe aus, um nach diesem Objektblock zu suchen, und entfernt alle Objekte. Ist der Block jedoch zu groß, "gibt" der GC die Prüfung auf und geht davon aus, dass die Objekte erreicht werden können. Nun stellen Sie sich vor, wenn Sie so etwas haben:
Dies ist die Insel der Referenzen. Das System würde viel Speicher beanspruchen und aufgrund der Komplexität nicht vom GC erfasst werden. Das hört sich ziemlich schlecht an, oder? Es kann jedoch leicht vermieden werden. Stellen Sie nur sicher, dass Sie alle Verweise auf und von einem Objekt gelöscht haben, und dann werden solche gruseligen Dinge nicht passieren!
Das ist es erstmal. In diesem Quick Tip haben wir gelernt, dass wir unseren Code verbessern und effizienter machen können, um Verzögerungen und Speicherprobleme zu reduzieren und somit stabiler zu gestalten. Um dies zu erreichen, müssen wir verstehen, wie das Referenzieren von Objekten in AS3 funktioniert und wie Sie davon profitieren können, damit der GC in unserer Anwendung ordnungsgemäß funktioniert. Trotz der Tatsache, dass wir unsere Anwendung verbessern können, müssen wir vorsichtig sein - sonst kann es noch unübersichtlicher und langsamer werden!
Ich hoffe, Ihnen hat dieser einfache Tipp gefallen. Wenn Sie Fragen haben, schreiben Sie unten einen Kommentar!