ARC frißt Memory

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

  • ARC frißt Memory

    Hallo,
    dies ist mein erster Beitrag hier und man möge mir meine Unkenntniss verzeihen...
    Trotz vieler Literatur und vielem Lesen im hiesigen Forum weigert sich mein Hirn beharrlich, daß Objective-C Memory-Management zu verstehen. Also zum Thema. Habe mir eine Anwendung geschrieben, die csv Files einließt und die Werte graphisch aufbereitet. Bin soweit eigentlich fertig. Die Diagramme von tausenden von Werten werden in Echtzeit dargestellt mit Zoomfunktion. Bin ein wenig stolz darauf ;) Nachedm ich soweit fertig war mit testen, habe ich die App auf eine große Zahl von csv Files losgelassen, die nacheinander eingelesen und verarbeitet werden. Dabei fiel mir auf, daß der Speicherverbrauch der App während des Einlesens dramatisch ansteigt und der Mac anfängt zu swappen (>2GB!). Ich habe den Code zum verifizieren soweit abgespeckt, daß der untere Schnipsel übrigbleibt und einer TestApp innerhalb von applicationDidFinishLaunching: mit zig csv Files zyklisch aufgerufen wird. Ist applicationDidFinishLaunching: beendet fällt der benutzte Spiecher wieder auf MB Niveau zurück. ARC ist aktiviert.


    Was mache ich falsch?

    Quellcode

    1. +(void)importPerformanceFile:(NSString *)datei {
    2. int i;
    3. NSString *dateiInhalt = [NSString stringWithContentsOfFile: datei encoding:NSUTF8StringEncoding error:nil];
    4. NSArray *dateiZeilen = [dateiInhalt componentsSeparatedByString:@"\r\n"];
    5. for (i = 0; i < [dateiZeilen count]; i++) {
    6. NSString *dateiZeile = [[dateiZeilen objectAtIndex: i] stringByReplacingOccurrencesOfString: @"\"" withString: @""];
    7. NSArray *csvContent = [dateiZeile componentsSeparatedByString: @","];
    8. }
    9. }
    Alles anzeigen
  • Na du allozierst tausende NSString's und NSArray's, die auch mit ARC so lange bestehen bleiben, bis deren Scope, also die Methode abgelaufen ist. Das hier sollte besser klappen:

    Quellcode

    1. +(void)importPerformanceFile:(NSString *)datei {
    2. int i;
    3. NSString *dateiInhalt = [NSString stringWithContentsOfFile: datei encoding:NSUTF8StringEncoding error:nil];
    4. NSArray *dateiZeilen = [dateiInhalt componentsSeparatedByString:@"\r\n"];
    5. NSString *dateiZeile;
    6. NSArray *csvContent;
    7. for (i = 0; i < [dateiZeilen count]; i++) {
    8. dateiZeile = [[dateiZeilen objectAtIndex: i] stringByReplacingOccurrencesOfString: @"\"" withString: @""];
    9. csvContent = [dateiZeile componentsSeparatedByString: @","];
    10. }
    11. }
    Alles anzeigen


    So machst du nur genau einen NSString und nur ein NSArray und verwendest die dann wieder.
  • Airrud schrieb:

    Was mache ich falsch?


    Quellcode

    1. +(void)importPerformanceFile:(NSString *)datei {
    2. // ...
    3. for (i = 0; i < [dateiZeilen count]; i++) {
    4. NSString *dateiZeile = [[dateiZeilen objectAtIndex: i] stringByReplacingOccurrencesOfString: @"\"" withString: @""];
    5. NSArray *csvContent = [dateiZeile componentsSeparatedByString: @","];
    6. }
    7. }


    Du hast dann also pro Zeile jeweils einen String und ein Array.
    2GB CSV besteht sicherlich aus einigen tausend Zeilen, also einige tausend Strings und einige tausend Arrays.

    Mir fällt da nix Besseres ein als @autoreleasepool{} drum zu werfen.
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P
  • scriptedSheep schrieb:

    Warum nicht das was ich vorgeschlagen habe?

    Deine Lösung klingt zunächst richtig.
    Nur: die Objekte sind "irgendwie autoreleased". Das Problem ist dasselbe - sie bleiben bis zum Wiedereintritt in die Runloop im Speicher.
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P
  • Wenn ich den original Post richtig verstanden habe, dann wird in applicationDidFinishLaunching: wiederholt die Methode importPerformanceFile: aufgerufen. Die darin im AutoreleasePool erzeugten Objekte werden jedoch erst am Ende des Event Loop ( also nach Beendigung der Methode applicationDidFinishLaunching: ) gelöscht, so dass sich der AutoreleasePool weiter füllt und die Objekte weiterhin Speicherplatz belegen.
  • scriptedSheep schrieb:

    Na du allozierst tausende NSString's und NSArray's, die auch mit ARC so lange bestehen bleiben, bis deren Scope, also die Methode abgelaufen ist. Das hier sollte besser klappen:

    Quellcode

    1. +(void)importPerformanceFile:(NSString *)datei {
    2. int i;
    3. NSString *dateiInhalt = [NSString stringWithContentsOfFile: datei encoding:NSUTF8StringEncoding error:nil];
    4. NSArray *dateiZeilen = [dateiInhalt componentsSeparatedByString:@"\r\n"];
    5. NSString *dateiZeile;
    6. NSArray *csvContent;
    7. for (i = 0; i < [dateiZeilen count]; i++) {
    8. dateiZeile = [[dateiZeilen objectAtIndex: i] stringByReplacingOccurrencesOfString: @"\"" withString: @""];
    9. csvContent = [dateiZeile componentsSeparatedByString: @","];
    10. }
    11. }
    Alles anzeigen


    So machst du nur genau einen NSString und nur ein NSArray und verwendest die dann wieder.

    Ich hoffe die oben aufgeführte Aussage "So machst du nur genau einen NSString und nur ein NSArray und verwendest die dann wieder." zum Source Code ist nicht ernst gemeint, oder?
  • scriptedSheep schrieb:

    Na du allozierst tausende NSString's und NSArray's, die auch mit ARC so lange bestehen bleiben, bis deren Scope, also die Methode abgelaufen ist. Das hier sollte besser klappen:

    Quellcode

    1. +(void)importPerformanceFile:(NSString *)datei {
    2. int i;
    3. NSString *dateiInhalt = [NSString stringWithContentsOfFile: datei encoding:NSUTF8StringEncoding error:nil];
    4. NSArray *dateiZeilen = [dateiInhalt componentsSeparatedByString:@"\r\n"];
    5. NSString *dateiZeile;
    6. NSArray *csvContent;
    7. for (i = 0; i < [dateiZeilen count]; i++) {
    8. dateiZeile = [[dateiZeilen objectAtIndex: i] stringByReplacingOccurrencesOfString: @"\"" withString: @""];
    9. csvContent = [dateiZeile componentsSeparatedByString: @","];
    10. }
    11. }
    Alles anzeigen


    So machst du nur genau einen NSString und nur ein NSArray und verwendest die dann wieder.

    scriptedSheep schrieb:

    Na du allozierst tausende NSString's und NSArray's, die auch mit ARC so lange bestehen bleiben, bis deren Scope, also die Methode abgelaufen ist. Das hier sollte besser klappen:

    Quellcode

    1. +(void)importPerformanceFile:(NSString *)datei {
    2. int i;
    3. NSString *dateiInhalt = [NSString stringWithContentsOfFile: datei encoding:NSUTF8StringEncoding error:nil];
    4. NSArray *dateiZeilen = [dateiInhalt componentsSeparatedByString:@"\r\n"];
    5. NSString *dateiZeile;
    6. NSArray *csvContent;
    7. for (i = 0; i < [dateiZeilen count]; i++) {
    8. dateiZeile = [[dateiZeilen objectAtIndex: i] stringByReplacingOccurrencesOfString: @"\"" withString: @""];
    9. csvContent = [dateiZeile componentsSeparatedByString: @","];
    10. }
    11. }
    Alles anzeigen


    So machst du nur genau einen NSString und nur ein NSArray und verwendest die dann wieder.

    scriptedSheep schrieb:

    Na du allozierst tausende NSString's und NSArray's, die auch mit ARC so lange bestehen bleiben, bis deren Scope, also die Methode abgelaufen ist. Das hier sollte besser klappen:

    Quellcode

    1. +(void)importPerformanceFile:(NSString *)datei {
    2. int i;
    3. NSString *dateiInhalt = [NSString stringWithContentsOfFile: datei encoding:NSUTF8StringEncoding error:nil];
    4. NSArray *dateiZeilen = [dateiInhalt componentsSeparatedByString:@"\r\n"];
    5. NSString *dateiZeile;
    6. NSArray *csvContent;
    7. for (i = 0; i < [dateiZeilen count]; i++) {
    8. dateiZeile = [[dateiZeilen objectAtIndex: i] stringByReplacingOccurrencesOfString: @"\"" withString: @""];
    9. csvContent = [dateiZeile componentsSeparatedByString: @","];
    10. }
    11. }
    Alles anzeigen


    So machst du nur genau einen NSString und nur ein NSArray und verwendest die dann wieder.
    Hallo und danke für die Antwort. Habe ich gleich probiert, allerdings leider mit dem gleichen schlechten Ergebniss.
    Gruß, Matthias
  • Wenn Du die Methode importPerformanceFile: mehrfach innerhalb eines Event-Loops aufrufst, dann ist ein eigener AutoreleasePool die einfachste Lösung.

    Kann sein, dass es mit ARC auch andere Lösungsmöglichkeiten für dieses Problem gibt. Da müssen dann die ARC Spezis Lösungen vorschlagen.

    Ein eigener AutoreleasePool wird jedoch mit und ohne ARC funktionieren.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von MCDan ()

  • Danke, für den Tip. Habe es abgeändert mit einem ARPool und siehe da, es funktioniert.
    Der Speicher wird nicht mehr gefressen. Ich hoffe, das klappt auch noch, wenn ich es in die große App einbaue. Habe erst mit __weak experimentiert - ohne Erfolg. Bei Apple hab ich Danke deines Hinweises, daß hier gefunden:

    ARP Class Reference

    Quellcode

    1. +(void)importPerformanceFile:(NSString *)datei {
    2. @autoreleasepool {
    3. int i;
    4. NSString *dateiInhalt = [NSString stringWithContentsOfFile: datei encoding:NSUTF8StringEncoding error:nil];
    5. NSArray *dateiZeilen = [dateiInhalt componentsSeparatedByString:@"\r\n"];
    6. for (i = 0; i < [dateiZeilen count]; i++) {
    7. NSString *dateiZeile = [[dateiZeilen objectAtIndex: i] stringByReplacingOccurrencesOfString: @"\"" withString: @""];
    8. NSArray *csvContent = [dateiZeile componentsSeparatedByString: @","];
    9. }
    10. }
    11. }
    Alles anzeigen
  • gritsch schrieb:

    Airrud schrieb:

    gritsch schrieb:

    @Airrud dir ist aber hoffentlich auch klar dass man CSV-files nicht so einfach parsen kann!
    Es sind "flache" csv Files. Also eigentlich Textfiles mit kommaseparierten Werten. Das Einlesen an sich und Aufbereiten klappt ja auch wunderbar.


    und was ist wenn >"< darin vorkommen oder >,< ?


    >"< wird ja durch Nichts ersetzt:

    Quellcode

    1. NSString *dateiZeile = [[dateiZeilen objectAtIndex: i] stringByReplacingOccurrencesOfString: @"\"" withString: @""];


    Und die Kommas sind ja der eigentliche Trenner zwischen den Spalten. Die Files haben immer den gleichen Aufbau ohne Überraschungen. Es sind die IO Meßwerte eines großen Speicherarrays mit tausenden von Disks.
  • gritsch schrieb:

    aha, also kann sowas nicht vorkommen?

    "Knight Rider 3000 \"Kitt\", the new generation"

    denn daraus würde mit deiner methode nur noch "Knight Rider 3000 Kitt" und im nächsten feld würde dann "the new generation" stehen...

    gritsch schrieb:

    aha, also kann sowas nicht vorkommen?

    "Knight Rider 3000 \"Kitt\", the new generation"

    denn daraus würde mit deiner methode nur noch "Knight Rider 3000 Kitt" und im nächsten feld würde dann "the new generation" stehen...


    Nee, sowas kommt nicht vor. Die Files werden maschinell (vom Storagesilo) generiert mit festen Regeln.
  • Airrud schrieb:

    Danke, für den Tip. Habe es abgeändert mit einem ARPool und siehe da, es funktioniert.

    Ach... ^^

    Worauf der gritsch hinaus will: du kannst bei CSV nicht davon ausgehen, dass sie immer genau SO aussehen.
    Du prüfst jetzt auf Trennzeichen , für Datensätze
    " als Einschlusszeichen der Datensätze
    Windows-Zeilenumbruch für Zeilen

    Messwerte mit
    1,345;2,345;2,98\n
    9,876;0,953;1,22

    werden in mehreren Fällen zu Fehlern führen.
    In deinem Fall mag es noch egal sein, doch wenn du die Anwendung auch an andere Menschen als an dich selbst vertreiben willst, solltest du das bedenken. ;)
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P
  • Lucas de Vil schrieb:

    Airrud schrieb:

    Danke, für den Tip. Habe es abgeändert mit einem ARPool und siehe da, es funktioniert.

    Ach... ^^

    Worauf der gritsch hinaus will: du kannst bei CSV nicht davon ausgehen, dass sie immer genau SO aussehen.
    Du prüfst jetzt auf Trennzeichen , für Datensätze
    " als Einschlusszeichen der Datensätze
    Windows-Zeilenumbruch für Zeilen

    Messwerte mit
    1,345;2,345;2,98\n
    9,876;0,953;1,22

    werden in mehreren Fällen zu Fehlern führen.
    In deinem Fall mag es noch egal sein, doch wenn du die Anwendung auch an andere Menschen als an dich selbst vertreiben willst, solltest du das bedenken. ;)


    Ja, ihr habt natürlich Recht. Aktuell ist die App für ein ganz spezifisches Problem. Wenn sie universell funktionieren soll, muß sowas berücksichtigt werden.