memory leak

  • hallo leute,

    die folgenden methoden werden aufgerufen um pfade von bildern aus einer ordnerstruktur auszulesen. es sind rund 18500 bilder deren pfade in einen string gescrieben werden. der string soll am ende in eine datei geschrieben werden.
    das problem an der ganzen sache: die methoden produzieren ein riesen speicherleck und ich hab keine ahnung wo. ich hoffe ihr könnt mir da weiter helfen.

    Quellcode

    1. - (void) stockUpdate
    2. {
    3. NSString *path = [preferences objectForKey:@"prefs_stockPath"];
    4. if (![fileManager fileExistsAtPath:path])
    5. return; ///////////////////////////////////////// needs a message
    6. if (fileList)
    7. [fileList release];
    8. fileList = [[NSString string] retain];
    9. [self updateStock:@""];
    10. [fileList writeToFile:[[preferences objectForKey:@"prefs_dbPath"] stringByAppendingPathComponent:@"fileList.txt"] atomically:YES];
    11. [self showAll];
    12. NSRunAlertPanel(@"Think:Image", @"Stockupdate done!", @"OK", NULL, NULL);
    13. }
    14. - (void) updateStock: (NSString*) path
    15. {
    16. BOOL isDir;
    17. NSString *tmpPath, *imagePath, *stockPath = [preferences objectForKey:@"prefs_stockPath"], *trust, *str_imageID;
    18. NSString *virtFilePath, *sqlCommand, *fileName, *trustCode;
    19. NSArray *resultArray = [[NSArray array]retain];
    20. NSMutableArray *tmpArray = [[NSMutableArray array]retain];
    21. int i,j;
    22. tmpPath = [stockPath stringByAppendingPathComponent:path];
    23. tmpArray = [NSMutableArray arrayWithArray: [fileManager directoryContentsAtPath: tmpPath]];
    24. [tmpArray removeObject:@".DS_Store"];
    25. for (i=0;i<[tmpArray count];i++){
    26. imagePath = [tmpPath stringByAppendingPathComponent:[tmpArray objectAtIndex:i]];
    27. if ([fileManager fileExistsAtPath:imagePath isDirectory:&isDir] && isDir) {
    28. [self updateStock:[path stringByAppendingPathComponent:[tmpArray objectAtIndex:i]]];
    29. } else {
    30. fileName = [[imagePath lastPathComponent]stringByDeletingPathExtension];
    31. trust = [fileName substringWithRange:NSMakeRange(0,8)];
    32. [status setStringValue:[NSString stringWithFormat:@"%@ (%i/%i)",trust,i+1,[tmpArray count]]];
    33. [status display];
    34. virtFilePath = [path stringByAppendingString:@"/"];
    35. fileList = [fileList stringByAppendingFormat:@"%@%@\r",virtFilePath,[imagePath lastPathComponent]];
    36. }
    37. }
    38. [resultArray release];
    39. [tmpArray release];
    40. }
    Alles anzeigen


    ich weiss, das sind ein paar variablen drin die nicht benutzt werden. die brauche ich aber später noch.

    die zweite methode ist rekursiv. sie wird aufgerufen, wenn ein ordner gefunden wurde.

    sascha
  • Original von Michael
    In Zeile 23 erzeugst Du ein leeres NSMutableArray und retainst es. Die Referenz auf dieses Array überschreibst Du in Zeile 28 durch das Erzeugen eines weiteren NSMutableArray -> memory leak.

    Michael


    und Zeile 45 enthält dann ein release zu viel.


    t.
    Das iPhone sagt: "Zum Antworten streichen". Wie? Echt Jetzt? Muß ich erst die Wohnung streichen!?
  • mittlerweile sieht die methode so aus, sie "frisst" auch schon nicht mehr ganz so viel speicher, aber es muss noch ein leck geben. da der speicherbedarf stetig wächst. nur nicht mehr so schnell.

    Quellcode

    1. - (void) updateStock: (NSString*) path
    2. {
    3. BOOL isDir;
    4. NSString *tmpPath, *imagePath, *stockPath = [preferences objectForKey:@"prefs_stockPath"], *trust, *str_imageID;
    5. NSString *virtFilePath, *sqlCommand, *fileName, *trustCode;
    6. NSArray *resultArray;
    7. NSMutableArray *tmpArray;
    8. int i,j;
    9. tmpPath = [stockPath stringByAppendingPathComponent:path];
    10. tmpArray = [NSMutableArray arrayWithArray: [fileManager directoryContentsAtPath: tmpPath]];
    11. [tmpArray removeObject:@".DS_Store"];
    12. for (i=0;i<[tmpArray count];i++){
    13. imagePath = [tmpPath stringByAppendingPathComponent:[tmpArray objectAtIndex:i]];
    14. if ([fileManager fileExistsAtPath:imagePath isDirectory:&isDir] && isDir) {
    15. [self updateStock:[path stringByAppendingPathComponent:[tmpArray objectAtIndex:i]]];
    16. } else {
    17. fileName = [[imagePath lastPathComponent]stringByDeletingPathExtension];
    18. trust = [fileName substringWithRange:NSMakeRange(0,8)];
    19. [status setStringValue:[NSString stringWithFormat:@"%@ (%i/%i)",trust,i+1,[tmpArray count]]];
    20. [status display];
    21. virtFilePath = [path stringByAppendingString:@"/"];
    22. fileList = [fileList stringByAppendingFormat:@"%@%@\r",virtFilePath,[imagePath lastPathComponent]];
    23. }
    24. }
    25. [tmpArray release];
    26. }
    Alles anzeigen


    btw: kann man die zeilen 12 und 13 auch anders schreiben?
  • verstehe sowieso nicht, was ein -retain/release-Paar bei "convinient methods" (alle Klassenmethoden ohne ohne "init" im Namen) innerhalb einer Methode bringen soll.
    Das Array wird angelegt, der Retain-Counter steht auf 1, das Objekt geht in den Autorelease-Pool. Kommt das -retain hinzu, geht der Retain-Counter auf 2, folgt später ein release, geht der Retain-Counter wieder auf 1.
    Die Methode wird verlassen, das Programm tritt wieder in die "main run loop" ein und erst hier schlägt die Speicherverwaltung zu. Der Retain-Counter wird dekrementiert. Ist er dann 0, wird das Objekt gelöscht.
    Daraus folgt, dass bei "convinient methods" -retain nicht nur überflüssig ist, sondern auch riskant, weil man sich bei den "releases" vertuen kann.
    cu
    Peter
  • Original von macuser
    mittlerweile sieht die methode so aus, sie "frisst" auch schon nicht mehr ganz so viel speicher, aber es muss noch ein leck geben. da der speicherbedarf stetig wächst. nur nicht mehr so schnell.


    Nicht raten, messen!

    /Developer/Applications/Performance Tools/MallocDebug

    und

    /Developer/Applications/Performance Tools/ObjectAlloc

    helfen da weiter.

    Daß der Speicherbedarf stetig wächst, ist auch durchaus wahrscheinlich. Ein eigener NSAutoreleasePool in der Methode dürfte die Situation verbessern.

    t.
    Das iPhone sagt: "Zum Antworten streichen". Wie? Echt Jetzt? Muß ich erst die Wohnung streichen!?
  • Original von tsunamix
    Original von macuser
    btw: kann man die zeilen 12 und 13 auch anders schreiben?


    Zeile 12 könnte z.B. auch so aussehen:

    Quellcode

    1. tmpArray = [[fileManager directoryContentsAtPath: tmpPath] mutableCopy];



    t.


    ich meinte eher damit ob man die zeile 12 so schreibt, das zeile 13 überflüssig wird, also das man den ordnerinhalt ohne die unsichtbaren dateien ausliesst....
  • Original von macuser
    Original von tsunamix
    Daß der Speicherbedarf stetig wächst, ist auch durchaus wahrscheinlich. Ein eigener NSAutoreleasePool in der Methode dürfte die Situation verbessern.
    t.


    wie funktioniert das?


    Dazu gibt es ein schönes Beispiel in Apple-Doku:

    Quellcode

    1. – findMatchingObject:anObject
    2. {
    3. id match = nil;
    4. while (match == nil) {
    5. NSAutoreleasePool *subpool = [[NSAutoreleasePool alloc] init];
    6. /* Do a search that creates a lot of temporary objects. */
    7. match = [self expensiveSearchForObject:anObject];
    8. if (match != nil)
    9. [match retain]; /* Keep match around. */
    10. [subpool release];
    11. }
    12. return [match autorelease]; /* Let match go and return it. */
    13. }
    Alles anzeigen
    cu
    Peter
  • Original von macuser
    wie würde das auf meine methode angewand aussehen?
    ich hab das schonmal probiert aber das programm ist mit sig10 und 11 abgeschmiert....


    OK, habe noch ein Beispiel in der Apple-Doku gefunden, das deinem Fall sehr nahe kommt.

    Quellcode

    1. void main()
    2. {
    3. NSArray *args = [[NSProcessInfo processInfo] arguments];
    4. unsigned count, limit = [args count];
    5. for (count = 1; count < limit; count++){
    6. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    7. NSString *fileContents;
    8. NSString *fileName;
    9. fileName = [args objectAtIndex:count];
    10. fileContents = [[NSString alloc] initWithContentsOfFile:fileName];
    11. [fileContents autorelease];
    12. /* Process the file, creating and autoreleasing more objects. */
    13. [pool release];
    14. }
    15. /* Do whatever cleanup is needed. */
    16. exit (EXIT_SUCCESS);
    17. }
    Alles anzeigen
    cu
    Peter
  • Original von macuser
    ich denke mal das problem bei mir ist nicht die for schleife sondern die rekursion.
    wenn ich NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; noch vor die variablen deklaration und [pool release]; am ende der routine setze, bekomme ich besagte fehler.
    wie muss ich das machen?

    So müsste es gehen:

    Quellcode

    1. - (void) updateStock: (NSString*) path
    2. {
    3. BOOL isDir;
    4. NSString *tmpPath, *imagePath, *stockPath = [preferences objectForKey:@"prefs_stockPath"], *trust, *str_imageID;
    5. NSString *virtFilePath, *sqlCommand, *fileName, *trustCode;
    6. NSArray *resultArray;
    7. NSMutableArray *tmpArray;
    8. int i,j;
    9. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    10. tmpPath = [stockPath stringByAppendingPathComponent:path];
    11. tmpArray = [NSMutableArray arrayWithArray: [fileManager directoryContentsAtPath: tmpPath]];
    12. [tmpArray removeObject:@".DS_Store"];
    13. for (i=0;i<[tmpArray count];i++){
    14. imagePath = [tmpPath stringByAppendingPathComponent:[tmpArray objectAtIndex:i]];
    15. if ([fileManager fileExistsAtPath:imagePath isDirectory:&isDir] && isDir) {
    16. [self updateStock:[path stringByAppendingPathComponent:[tmpArray objectAtIndex:i]]];
    17. } else {
    18. fileName = [[imagePath lastPathComponent]stringByDeletingPathExtension];
    19. trust = [fileName substringWithRange:NSMakeRange(0,8)];
    20. [status setStringValue:[NSString stringWithFormat:@"%@ (%i/%i)",trust,i+1,[tmpArray count]]];
    21. [status display];
    22. virtFilePath = [path stringByAppendingString:@"/"];
    23. fileList = [fileList stringByAppendingFormat:@"%@%@\r",virtFilePath,[imagePath lastPathComponent]];
    24. }
    25. }
    26. [pool release];
    27. }
    Alles anzeigen
    Michael
  • Original von macuser
    wie würde das auf meine methode angewand aussehen?
    ich hab das schonmal probiert aber das programm ist mit sig10 und 11 abgeschmiert....


    Du bekommst den Absturz, weil Du Dein temporäres Array einmal zu oft released. Ich glaube, ich hatte in einem früheren Posting schon darauf hingewiesen. Dein überflüssiges [tempArray release] ist daran schuld.

    Der Grund ist folgender:

    Objekte, die mit einer 'Convenience Methode' erzeugt werden, z.B. [NSArray array], werden dem/ einem Autorelease Pool hinzugefügt. Dieser Pool erhöht den Referenzzähler (reference count) des Objektes um Eins. Wenn der Autorelease Pool selbst dealloziert wird, erhält jedes Object in diesem Pool ein release. Wenn man vor der Zerstörung eines Autorelease Pools ein in ihm enthaltenes Objekt released und dadurch das Objekt auch dealloziert wird, fällt das erstmal nicht weiter auf. Erst wenn der Autorelease Pool dieses Objekt, was ja ganz korrekt ist, bei seiner Deallozierung releasen will, greift er auf Speicher zurück, der ja schon (an der falschen Stelle) freigegeben wurde. Bang! Ein Release zu viel.

    Jedes Cocoa Programm muß mindestens ein Autorelease Pool haben. In Programmen mit graphischer Oberfläche kümmert sich Apple in der Funktion NSApplicationMain() darum. Wann dieser General-Autorelease Pool seinen regelmäßigen Hausputz macht und seine Müllobjekte released ist Apples 'Geheimnis'. Das kann schon einige Zeit dauern und der Speicherbedarf kann somit mit der Zeit wachsen. Will man in einer Methode diesen Autorelease-Wasserkopf klein halten, bettet man den Code in einen eigenen Pool ein....

    t.
    Das iPhone sagt: "Zum Antworten streichen". Wie? Echt Jetzt? Muß ich erst die Wohnung streichen!?
  • Original von tsunamix
    Objekte, die mit einer 'Convenience Methode' erzeugt werden, z.B. [NSArray array], werden dem/ einem Autorelease Pool hinzugefügt. Dieser Pool erhöht den Referenzzähler (reference count) des Objektes um Eins.

    Nein, das macht er Pool nicht. Ein Autorelease Pool sammelt nur alle Objekte, denen eine release-Message geschickt werden soll, wenn der Pool selbst released wird. Der Reference-Counter bleibt unverändert.

    Original von tsunamix
    Wann dieser General-Autorelease Pool seinen regelmäßigen Hausputz macht und seine Müllobjekte released ist Apples 'Geheimnis'.

    Man weiß aber, dass dies bei jedem Durchlauf der Eventloop einmal passiert. Üblicherweise am Ende. Wenn Sascha also seine rekursive Methode durch einen Klick auf einen Button aufruft, dann ist die Eventloop frühestens zu ende, wenn alle rekursiven Aufrufe zurückkehren. Da können sich dann natürlich diverse Objekte ansammeln.

    Michael
  • Original von Michael
    So müsste es gehen:

    Quellcode

    1. - (void) updateStock: (NSString*) path
    2. {
    3. BOOL isDir;
    4. NSString *tmpPath, *imagePath, *stockPath = [preferences objectForKey:@"prefs_stockPath"], *trust, *str_imageID;
    5. NSString *virtFilePath, *sqlCommand, *fileName, *trustCode;
    6. NSArray *resultArray;
    7. NSMutableArray *tmpArray;
    8. int i,j;
    9. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    10. tmpPath = [stockPath stringByAppendingPathComponent:path];
    11. tmpArray = [NSMutableArray arrayWithArray: [fileManager directoryContentsAtPath: tmpPath]];
    12. [tmpArray removeObject:@".DS_Store"];
    13. for (i=0;i<[tmpArray count];i++){
    14. imagePath = [tmpPath stringByAppendingPathComponent:[tmpArray objectAtIndex:i]];
    15. if ([fileManager fileExistsAtPath:imagePath isDirectory:&isDir] && isDir) {
    16. [self updateStock:[path stringByAppendingPathComponent:[tmpArray objectAtIndex:i]]];
    17. } else {
    18. fileName = [[imagePath lastPathComponent]stringByDeletingPathExtension];
    19. trust = [fileName substringWithRange:NSMakeRange(0,8)];
    20. [status setStringValue:[NSString stringWithFormat:@"%@ (%i/%i)",trust,i+1,[tmpArray count]]];
    21. [status display];
    22. virtFilePath = [path stringByAppendingString:@"/"];
    23. fileList = [fileList stringByAppendingFormat:@"%@%@\r",virtFilePath,[imagePath lastPathComponent]];
    24. }
    25. }
    26. [pool release];
    27. }
    Alles anzeigen
    Michael



    leider nicht. ich hab auch versucht die beiden sachen innerhalb der for schleife zu schreiben,
    aber der gleiche fehler. das app stürzt immer mit sig 10 oder 11 ab.