Verbrauch von RAM steigt ins Endlose.

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Verbrauch von RAM steigt ins Endlose.

    Hallo!

    Also ich habe ein Programm geschrieben dass mir erst eine JSON-Datei aus dem Internet holt. Das passiert 838 mal. Danach hol ich mir mit der MediaWiki API aus einem Wiki eine Seite pro Eintrag (Es sind momentan genau 41882 Einträge) und lese aus dem XML ein Attribut aus. Das schreibe ich erstmal in ein NSDictionary und dann in eine csv. Ich schreib das NSDictionary sobald es vollständig ist, schreib ich es in die Datei und release das sofort wieder, wenn ich neue Werte eintrage.

    Hört sich erstmal nach ca 20 Zeilen an aber da die JSON-Datei voller Müll ist und so unnützen Sachen wie "\/" und viele Werte, die ich nicht brauche (Die ganze Sache ist mal wieder ein Fall von schlechter Umsetzung... Da muss man erst 40k Einträge parsen damit man die API ordentlich benutzen kann) und die müssen raus. Dann kommen da noch Werte zu die komische Sonderzeichen haben. û, ó und auch mal + und % die man erstmal in diese coolen %irgentwas-Werte umwandeln muss.

    Aber genug geheult...

    Der RAM-Verbrauch steigt einfach ins Unendliche und ich weiß nicht warum. Es gibt laut Instruments keine Zombies und es wird auch nicht immer mit jedem loop eine Variable erweitert. Ich lasse das Programm jetzt 28 Minuten laufen und bin bei 138MB Live Bytes, 734MB Overall Bytes und 180MB in der Aktivitätsanzeige. Das ist erst mal nicht schlimm aber bei 40k Einträgen dauert das 7 Stunden (der Webserver ist ein bisschen langsam) und irgentwie sollte das ja auch nicht passieren. Selbst wenn man bei 8 GB RAM eigentlich keine großen Probleme auf dauer bekommen sollte, nervt da Schon...

    Ich habe keine Ahnung wo der Fehler ist. Der Code ist unübersichtlich und eigentlich nur Quick and Dirty weil ich das Programm nicht so oft brauchen werde.

    Quellcode

    Ich kann den Quellcode leider nicht einfach so posten. Das würde das 10000 Zeichen Limit sprengen.

    MfG Asyx
  • Du solltest dringend etwas zum Thema Speichermanagement lesen.

    Hier einige Beispiele für Memory Leaks in deinem Code:

    Quellcode

    1. NSString* json_string = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];


    Quellcode

    1. // Initialisation for clean NSStrings.
    2. NSString* imageClean = [[[NSString alloc] init] retain];
    3. NSString* linkClean = [[[NSString alloc] init] retain];
    4. NSString* rarenessClean = [[[NSString alloc] init] retain];


    Du führst niemals ein release aus für diese Objekte. Daher steigt der Speicherverbrauch zwangsläufig an.

    Gibt noch mehr Stellen in deinem Code, aber ich habe keine Lust alles herauszukopieren.
  • Kann Pennywise81 nur zustimmen. Versuche mal die App mit "Build and Analyze" zu übersetzen. Da wirst Du sicherlich einige Hinweise zu Problemen mit dem Speichermanagement finden.

    Weiterhin solltest Du Dich noch mal mit den Basics beschäftigen, um z.B. so etwas zu vermeiden:

    Quellcode

    1. NSString* imageDirt = [[NSString alloc] init];
    2. ...
    3. // Get the right attribute.
    4. for (NSXMLNode* attr in [imgTag attributes]) {
    5. if ([[attr name] isEqualToString:@"src"]) {
    6. imageDirt = [attr stringValue];

    Ok, eigentlich sollte Instruments dieses Problem erkennen und anzeigen, aber "Build and Analyze" sollte auf alle Fälle etwas dazu sagen. In Zeile 1 erzeugst Du ein NSString Objekt und in Zeile 6 weist Du der Referenz ein anderes NSString Objekt zu und erzeugst somit ein Memory-Leak für das Objekt aus Zeile 1 welches nicht mehr referenziert und somit nicht mehr freigegeben werden kann.


    Quellcode

    1. // Initialisation for clean NSStrings.
    2. NSString* imageClean = [[[NSString alloc] init] retain];
    3. ...
    4. // Clean the image and link.
    5. imageClean = [imageDirt stringByReplacingOccurrencesOfString:@"\\/" withString:@"/"];

    Was soll ein doppeltes retain in Zeile 2 bringen? Die Methode stringByReplacingOccurrencesOfString: liefert Dir ein neues NSString Objekt zurück. Die Referenz auf das in Zeile 2 erzeugte NSString Objekt geht Dir in Zeile 5 natürlich verloren und Du erzeugst ein Memory-Leak.


    Quellcode

    1. @synthesize values;
    2. ...
    3. self.values = [[NSMutableDictionary alloc] init];
    4. ...
    5. [self.values release];

    Wie sieht Deine Property-Definition für values aus? Verwendest Du ein retain oder ein assign Property?
  • Ok... Das lustige ist, dass den meisten Schwachsinn den ich da in Sachen Memory Management gemacht habe nur gemacht habe, weil Instruments gemeckert hat und ich EXC_BAD_ACCESS in [pool drain]; bekommen habe und mit dem ganzen Müll ist das nicht mehr passiert.

    Vielleicht sollte ich eher lernen wie man mit Instruments umgeht =).

    Die Propertys sind alle retain und nonatomic.

    Danke
  • asyx schrieb:

    Die Propertys sind alle retain und nonatomic.

    Ok, wenn Du retain Properties verwendest, dann solltest Du values z.B. so:

    Quellcode

    1. self.values = [NSMutableDictionary dictionary];

    oder so:

    Quellcode

    1. self.values = [[[NSMutableDictionary alloc] init] autorelease];

    initalisieren und im dealloc so:

    Quellcode

    1. self.values = nil;

    freigeben.
  • So ich hab jetzt alles an Memory Zeugs gefixt aber der RAM-Verbrauch steigt noch. Aber jetzt hab ich eine böse Idee... Wird das, was im Terminal steht auch zu dem RAM-Verbrauch vom Programm gerechnet? Ich hab immer "x/n\n", "x+1/n\n", "x+2/\n" usw. im Terminal stehen und das würde en ganzen Thread unsinnig machen und das wäre voll peinlich weil mir das nicht früher eingefallen ist =/
  • Nein, die Ausgabe in der Console beeinflusst den Speicherverbrauch der App nicht.

    Es wird wohl daran liegen, dass Du keine eigenen Autorelese Pools verwendest und der komplette Code zum Verarbeiten der Daten in einem RunLoop Durchlauf ausgeführt wird. Der automatische Autorelease Pool pro RunLoop Durchlauf wird erst beim Beenden des Durchlaufs geleert.

    Lege in der Methode getValues: mal einen eigenen Autorelese Pool an, dann sollte auch der Speicherverbrauch sinken.