Speicher Warnung und crash (ARC) - wie zu erkennen, warum es passiert?
Habe ich angefangen, die ARC seit kurzem und dann habe ich die Schuld für jedes einzelne problem mit dem Arbeitsspeicher. 🙂 Vielleicht könnten Sie mir helfen, besser zu verstehen, was ich falsch mache.
Mein Aktuelles Projekt ist über CoreGraphics eine Menge - Diagramme zeichnen, Ansichten gefüllt mit miniaturen und so weiter. Ich glaube, wäre es kein problem, während mit der manuellen Speicherverwaltung, außer vielleicht ein paar zombies... Aber jetzt, Applikation einfach so abstürzt jedes mal, wenn ich versuche, um entweder eine Menge von thumbnails oder zeichnen ein wenig komplizierter, chart.
Während der profiling-Instrumente kann ich sehen, ein enorm hoher Wert im Residenten Speicher sowie in der schmutzigen. Heap Analyse zeigt eher beunruhigend unregelmäßig wachsen...
Beim zeichnen nur ein paar Vorschaubilder, Residenten Speicher wächst für über 200 MB. Wenn alles gezeichnet ist, Speicher sinkt wieder auf fast den gleichen Wert wie vor dem auftragen. Jedoch, eine Menge miniaturen, ein Wert in Residenten Speicher ist höher als 400 MB und natürlich stürzt die app. Ich habe versucht, zu begrenzen Sie die Anzahl der miniaturen gezeichnet, in der gleichen Zeit (NSOperationQueue und seine maxConcurrentOperationCount), aber wie die Freisetzung von so viel Arbeitsspeicher scheint mal etwas mehr Zeit nehmen, hat es nicht wirklich das Problem zu lösen.
Jetzt meine app im Grunde gar nicht funktionieren, da die realen Daten arbeitet mit einer Menge von komplizierten charts = Menge von thumbnails.
Jeder Miniaturansicht wird erstellt, mit diesem code hab ich von hier: (Kategorie UIImage)
+ (void)beginImageContextWithSize:(CGSize)size
{
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
if ([[UIScreen mainScreen] scale] == 2.0) {
UIGraphicsBeginImageContextWithOptions(size, YES, 2.0);
} else {
UIGraphicsBeginImageContext(size);
}
} else {
UIGraphicsBeginImageContext(size);
}
}
+ (void)endImageContext
{
UIGraphicsEndImageContext();
}
+ (UIImage*)imageFromView:(UIView*)view
{
[self beginImageContextWithSize:[view bounds].size];
BOOL hidden = [view isHidden];
[view setHidden:NO];
[[view layer] renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
[self endImageContext];
[view setHidden:hidden];
return image;
}
+ (UIImage*)imageFromView:(UIView*)view scaledToSize:(CGSize)newSize
{
UIImage *image = [self imageFromView:view];
if ([view bounds].size.width != newSize.width ||
[view bounds].size.height != newSize.height) {
image = [self imageWithImage:image scaledToSize:newSize];
}
return image;
}
+ (UIImage*)imageWithImage:(UIImage*)image scaledToSize:(CGSize)newSize
{
[self beginImageContextWithSize:newSize];
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
[self endImageContext];
return newImage;
}
Gibt es eine andere Möglichkeit, die würde es nicht Essen, so viel Speicher oder ist etwas wirklich falsch mit dem code bei der Verwendung von ARC?
Dem anderen Ort, wo der Speicher Warnung + crash passiert ist, wenn es zu viel Neuzeichnen von einer beliebigen Ansicht. Es muss nicht schnell sein, nur viele Male. Speicher-stacks, bis es kracht und ich bin nicht in der Lage, etwas zu finden, wirklich für Sie verantwortlich. (Ich sehe eine wachsende resident/schmutzigen Speicher in der VM-Tracker und ein heap Wachstum in den Zuführungen instrument)
Meine Frage ist also: wie finden, warum es auch passiert? Mein Verständnis ist, wenn es keinen Besitzer für das Objekt gegeben, es ist freigegeben so bald wie möglich. Meine Inspektion der code schlägt eine Menge von Objekten werden nicht freigegeben, selbst wenn sehe ich keinen Grund, dass es passiert. Ich weiß nichts über irgendwelche behalten Zyklen...
Ich gelesen habe, durch die Umstellung auf ARC Release Notes, bbum ' s Artikel über heap Analyse und wahrscheinlich ein Dutzend anderer. Unterscheidet sich irgendwie heap Analyse mit und ohne ARC? Ich kann nicht scheinen, etwas nützliches zu tun mit seiner Ausgabe.
Danke für alle Ideen.
UPDATE: (nicht zwingen, alle Lesen, alle Kommentare und an mein Versprechen halten)
Durch gezielt immer durch meinen code und das hinzufügen von @autoreleasepool, wo es hatte keinen Sinn, Speicherverbrauch bekam gesenkt. Das größte problem war die Berufung UIGraphicsBeginImageContext
aus hintergrund-thread. Nach der Fixierung (siehe @Tammo Freese Antwort für details) Freigabe trat bald genug, um nicht zum Absturz einer app.
Meinem zweiten crash (verursacht durch viele Neuzeichnen des gleichen Diagramms), war völlig gelöst indem CGContextFlush(context)
am Ende meiner Zeichnung Methode. Schande über mich.
Eine kleine Warnung für jeden, der versucht, etwas ähnliches zu tun: OpenGL verwenden. CoreGraphics ist nicht schnell genug für Animation big Zeichnungen, vor allem nicht auf einem iPad 3. (die erste mit retina)
InformationsquelleAutor der Frage xius | 2012-07-20
Du musst angemeldet sein, um einen Kommentar abzugeben.
Deine Frage zu beantworten: Identifizieren von Problemen mit Speicher Warnungen und stürzt mit ARC funktioniert im Grunde wie vor mit manuellen retain-release (MRR). ARC nutzt
retain
release
undautorelease
wie MRR, es fügt nur die Anrufe für Sie, und hat einige Optimierungen, die sollte noch niedriger ist der Speicherverbrauch in einigen Fällen.In Bezug auf Ihr problem:
In der screenshot von Instrumenten, die Sie geschriebenes gibt allocation spikes sichtbar. In den meisten Fällen, die ich bisher begegnet sind, sind diese spikes verursacht wurden, autoreleased Objekte hängen um zu lange.
Sie erwähnten, dass Sie verwenden
NSOperationQueue
. Wenn Sie überschreiben-[NSOperationQueue main]
stellen Sie sicher, dass Sie wickeln den ganzen Inhalt der Methode in@autoreleasepool { ... }
. Ein autorelease pool bereits vorhanden ist, aber es ist nicht garantiert (und selbst wenn es einer ist, kann es sein, um länger als Sie denken).Wenn 1. hat nicht geholfen, und Sie haben eine Schleife, die die Bilder verarbeitet, wickeln Sie den inneren Teil der Schleife in
@autoreleasepool { ... }
so dass temporäre Objekte sind sofort aufwischen.Sie erwähnten, dass Sie verwenden
NSOperationQueue
. Seit iOS 4, Zeichnung zu einem Grafik-Kontext, in UIKit ist thread-sicher, aber wenn die Dokumentation stimmt,UIGraphicsBeginImageContext
sollte nur noch aufgerufen werden, auf dem main thread! Update: Die Doku Stand, dass seit iOS 4 die Funktion aufgerufen werden kann, aus jedem thread, der folgende ist eigentlich überflüssig!!! Sind Sie auf der sicheren Seite, erstellen Sie den Rahmen mitCGBitmapContextCreate
und um das Bild mitCGBitmapContextCreateImage
. Etwas entlang diesen Linien:InformationsquelleAutor der Antwort Tammo Freese
Also nichts, was Sie tun relativ zum Speicher-management - (es gibt keine!) sieht unpassend. Jedoch, Sie erwähnen mit NSOperationQueue. Diese UIGraphics... Anrufe sind markiert als nicht thread-sicher, aber andere haben ausgesagt, Sie sind wie von iOS 4 (ich kann nicht finden eine definitive Antwort auf diese, aber daran erinnern, dass dies wahr ist.
In jedem Fall sollten Sie nicht anrufen, diese Klasse Methoden von mehreren threads. Sie können eine serielle dispatch queue und feed alle arbeiten durch, um zu versichern, single-threaded-Nutzung.
Was hier noch fehlt ist natürlich das, was Sie tun mit den Bildern nach der Verwendung von Ihnen. Seine möglich, Sie halten Sie in irgendeiner Weise, die nicht offensichtlich sind. Hier sind einige tricks:
in Ihren Klassen, dass Sie zu viele Bilder, fügen Sie eine dealloc () - Methode, die meldet nur seinen Namen und eine id.
können Sie versuchen, fügen Sie einen dealloc zu UIImage, das gleiche zu tun.
versuchen zu fahren, Ihre app mit einem möglichst einfachen setup - wenigsten Bilder, etc - so können Sie überprüfen, dass in der Tat, dass die Bilder und Ihre Besitzer sind immer dealloc würde.
wenn Sie sicherstellen möchten, dass etwas veröffentlicht wird, setzen Sie die ivar oder Eigenschaft auf nil
Ich konvertiert eine 100-Datei-Projekt ARC im letzten Sommer, und es funktionierte perfekt aus der box. Ich habe Umgerechnet mehrere open-source-Projekte, die ARC als auch mit nur ein problem, wenn ich Sie falsch benutzt wird, zu überbrücken. Die Technik ist grundsolide.
InformationsquelleAutor der Antwort David H
Dies ist nicht die Antwort auf deine Frage, aber ich habe versucht, ähnliche Probleme zu lösen, lange bevor ARC eingeführt wurde.
Vor kurzem war ich arbeiten an einer Anwendung, die das caching von Bildern im Speicher und lösen Sie alle nach Erhalt der Speicher-Warnung. Das funktionierte gut, solange ich die Anwendung mit normaler Geschwindigkeit (nicht verrückter klopfen). Aber als ich anfing zu generieren, die eine Menge Ereignisse und viele Bilder angefangen zu laden, wird die Anwendung nicht geschafft, den Speicher Warnung und es stürzt ab.
Einmal habe ich eine test-Anwendung, die die Schaffung von vielen autoreleased Objekte nach dem Tippen auf eine Schaltfläche. Ich war in der Lage, Tippen Sie auf schneller (und Objekte erstellen) als das OS verwaltet werden, um den Speicher freizugeben. Der Speicher war langsam erhöhen, so dass nach einer signifikanten Zeit-oder einfach mit größeren Objekten würde ich sicherlich Absturz der Anwendung verursachen und Gerät neu starten (sieht wirklich gut ;)). Ich habe überprüft, dass die Verwendung von Instrumenten, die leider betroffenen den test gemacht und alles langsamer, aber ich nehme an, dies gilt auch, wenn Sie nicht mit Instrumenten.
Auf der anderen Gelegenheit war ich arbeiten an einem größeren Projekt, das ist sehr Komplex und hat eine Menge von UI-erstellt von code. Es hat auch eine Menge von string-Verarbeitung und niemand kümmerte sich um die Verwendung freigeben - es waren einige Tausende von autorelease-Aufrufe, wenn ich nachgeschaut, das Letzte mal. So nach 5 Minuten leicht umfangreiche Nutzung dieser Anwendung war es, die Absturz und dem Neustart des Gerätes.
Wenn ich richtig bin dann ist die OS/Logik, die verantwortlich ist für das eigentliche freigeben von Speicher ist nicht schnell genug oder nicht genügend hohe Priorität, um eine Anwendung abstürzt, wenn eine Menge von Speicher-Operationen durchgeführt werden. Ich habe nie bestätigt diese Vermutung und ich weiß nicht, wie dieses problem zu lösen, andere als nur die Senkung von Speicher zugewiesen.
InformationsquelleAutor der Antwort Mateusz