memory leak

  • RE: memory leak

    Mal generell, im Einzenen ist das ja shcon gut durchgesprochen:

    Ich habe hier schon einige Male einen Link zur RefCount-Regeln hingepostet. Wenn du die anwendest, passiert dir so etwas _nie_. (Es sei denn, du wendest sie falsch an. ;) Aber die Regeln haben den Vorteil, dass sie sich immer nur auf maximal 2 Zeilen Source beziehen, so dass das extrem leicht zu entdecken ist.)

    Auf diesen Fall bezogen:
    Wenn du etwas mit der cv (consrtuctor) erzeugst, mache _NIE_ einen retain oder release.
    Wenn du etwas mittels alloc erzeugst, mache _STETS_ einen autorelease.
    (Hiernach gilt unabhängig von der Art der Erzeugung _stets_, dass das erzeuigte Objekt nur noch einen RC von 1 hat, der vom ARP stammt. Das Objekt würde also bei dem nächsten Durchgang der RunLoop freigegeben.)
    Wenn du ein Objekt dauerhaft referenzierst, dann release den alten Verweis und retaine den neuen Verweis. Am besten in einer setter-Methode:

    Quellcode

    1. - (void)setObjekt:(Klasse*)objekt {
    2. [objekt retain]; // unbedingt die Reihenfolge beachten!
    3. [_objekt release];
    4. _objekt = objekt.
    5. }


    Ansonsten verwendest du die vier Methoden _gar_ _nicht_.

    Das funktioniert _IMMER_!
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Original von Michael
    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.


    Stimmt natürlich. Mea Culpa. War gestern wohl etwas spät.

    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


    So behauptet das Apple, ja, allein mir fehlt der Glaube. Ich kann das Gefühl nicht loswerden, daß die tatsächliche Implementierung sich da ein bißchen anders verhält (zB cached)...

    Aber das hat jetzt nichts mehr mit dem eigentlichen Thema zu tun...

    t.
    Das iPhone sagt: "Zum Antworten streichen". Wie? Echt Jetzt? Muß ich erst die Wohnung streichen!?
  • Außer der einen Instanz, die Du in -stockUpdate erzeugst, produziert diese Zeile kein Speicherloch. Die Zeile erzeugt lediglich einen Haufen NSString-Instanzen, die jedoch alle autoreleased sind und somit wird früher oder später der von ihnen belegte Speicher wieder freigegeben.
    Ein Speicherloch hinterlässt jedoch nicht mehr nutzbaren Speicher, der so lange das Programm läuft verloren ist.

    Michael
  • Original von Michael
    Außer der einen Instanz, die Du in -stockUpdate erzeugst, produziert diese Zeile kein Speicherloch. Die Zeile erzeugt lediglich einen Haufen NSString-Instanzen, die jedoch alle autoreleased sind und somit wird früher oder später der von ihnen belegte Speicher wieder freigegeben.
    Ein Speicherloch hinterlässt jedoch nicht mehr nutzbaren Speicher, der so lange das Programm läuft verloren ist.

    Michael


    es muss aber daran liegen, denn seit dem die zeile weg ist, frisst das app keinen speicher mehr. und das bei 18500 files in 2500 ordnern.
  • Das 18500 Instanzen von NSString einiges an Speicher benötigen (vor allem weil jede neue Instanz immer etwas größer als die vorhergehende ist) ist ja klar. Aber hast Du mal beobachtet, wie der Speicherverbrauch sich entwickelt, wenn so ein -stockUpdate fertig ist? Dann sollte der nämlich wieder zurückgehen.

    Und noch etwas. Die Zeile

    Quellcode

    1. trust = [fileName substringWithRange:NSMakeRange(0,8)];
    ist gefährlich. Wenn fileName kürzer als 8 Zeichen ist, dann hast Du ein Problem.

    Michael
  • muss mal ganz dumm fragen. Können Methoden überhaupt rekursiv sein?
    Ich hab das mal vor einiger Zeit mit dem Simpel-Fall Fakultät probiert und es ging nicht.
    Bin dann einfach auf eine C-Funktion ausgewichen, siehe auch mein Tutorial "Wie baue ich einen Taschenrechner, Teil 1".
    cu
    Peter
  • Original von WoSoft
    muss mal ganz dumm fragen. Können Methoden überhaupt rekursiv sein?
    Ich hab das mal vor einiger Zeit mit dem Simpel-Fall Fakultät probiert und es ging nicht.
    Bin dann einfach auf eine C-Funktion ausgewichen, siehe auch mein Tutorial "Wie baue ich einen Taschenrechner, Teil 1".


    naja, müssen sie ja, denn bei mir funktionierts ja (mittlerweile) einwandfrei.

    sascha
  • Na, so ganz falsch war das nicht.

    Ich mutmaße mal, dass da so aussieht:

    Quellcode

    1. In NSObject:
    2. - (void)autorelease {
    3. [autoreleasePool addObject:self]; // siehe unten
    4. [self release]; // RefCount-1
    5. }
    6. Im ARP:
    7. - (void)addObject:object {
    8. [myCollection (Array?) addObject:object]; // hier wird ein retin ausgeührt RefCount+1
    9. }


    Natürlich als "symbolischer Code" gemeint. Um sich einen ganz eigenen ARP zu basteln, bedarf es nichts anderes als eines NSArrays, das am Ende der Loop geleert wird und der Anweisungen: [array addObject] + [self release];

    Nur: Wozu sollte man das machen?

    So gesehen, retained das autorelease einmal und gibt einmal frei. Der RC bleibt also unverändert.

    Im Übrigen ist es garantiert, dass der ARP nicht vor Ende der Loop geleert wird. Wenn dem anders wäre, würde kein cm funktionieren. Und das wollen wir doch nicht, oder? ;)
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Original von macuser
    Original von WoSoft
    muss mal ganz dumm fragen. Können Methoden überhaupt rekursiv sein?
    Ich hab das mal vor einiger Zeit mit dem Simpel-Fall Fakultät probiert und es ging nicht.
    Bin dann einfach auf eine C-Funktion ausgewichen, siehe auch mein Tutorial "Wie baue ich einen Taschenrechner, Teil 1".


    naja, müssen sie ja, denn bei mir funktionierts ja (mittlerweile) einwandfrei.

    sascha


    OK, dann kannst du mir bestimmt auf die Sprünge helfen. Wie schreibt man diese Funktion als Methode?

    Quellcode

    1. double rechneFakRekursiv (double x)
    2. {
    3. if(x < 0)
    4. return 0.0;
    5. if(x == 0)
    6. return 1.0;
    7. return x * (rechneFakRekursiv(x - 1));
    8. }
    cu
    Peter
  • Original von macuser
    wenn ich da jetzt mal ganz blauäugig rangehen würde, würde ich das so machen (ohne es getestet zu haben :D)

    Quellcode

    1. - (double) rechneFakRekursiv: (double) x
    2. {
    3. if (x < 0)
    4. return 0.0;
    5. if (x == 0)
    6. return 1.0;
    7. return x * [self rechneFakRekursiv:(x - 1)];
    8. }


    Genau so habe ich das auch geschrieben und die Lösung verworfen, weil ich den Grund für folgender Fehler nicht finden konnte: Undefinde Symbol _rechneFakRekursiv.
    Man kann das provozieren, indem man die OriginallLösung nimmt (Rechner_3 im Tutorial) und deren Build Style auf Deployment stellt bzw. lässt.
    Dann schreibt man die Funktion samit Deklaration in eine Methode wie oben um (und den Aufruf natürlich auch) und hat den Fehler. Geht man dann auf Development zurück, ist der Fehler weg und geht man wieder auf Depoyment bleibt er weg.
    Kennt jemand den Grund für das Verhalten?
    cu
    Peter
  • Das Problem dabei wird gewesen sein, dass es als Instanz-Methode implementiert wurde. Um eine Instanz-Methode verwenden zu können, braucht man eben eine Instanz, die diese Methode kennt. Offensichtlich war das bei Deiner Implementierung nicht der Fall, weil der Linker die Methode nicht finden konnte. Im Development-Style fällt das nicht auf, da bei Xcode da ja Zero-Link aktiviert ist.

    Als Klassenmethode implementiert ist das Ganze etwas einfacher und funktioniert auch. Ein quick and dirty Projekt habe ich mal drangehängt.

    Michael
  • Danke Michael. Sehe ich soweit ein, nur eines ist mir unklar:
    Es funzte nur nicht, wenn ich im Deployment-Style beginne. Schalte ich dann auf Development, funktioniert es aus den von die genannten Gründen. Aber wenn ich dann wieder auf Deployment schalte, geht es dann auch.
    cu
    Peter
  • um mal wieder zum thema zurück zu kommen:
    ich noch ein kleines problem mit der rekursieven methode.
    im prinzip funktioniert sie jetzt da ich das array autorelease:

    Quellcode

    1. tmpArray = [[NSMutableArray arrayWithArray: [fileManager directoryContentsAtPath: tmpPath]] autorelease];


    aber am ende bekomme ich im runlog einen ganzen batzen gleiche fehlermeldungen, die zwar das
    prog nicht zum absturz bringen oder anders beeinflussen, es ist aber nicht gerade schoen.

    hier die fehlermeldung:

    Quellcode

    1. *** malloc[554]: error for object 0x5525950: Double free


    wo liegt da der fehler?

    sascha
  • Der Fehler liegt beim -autorelease. Das Array, welches Dir die Methode -arrayWithArray: zurückliefert ist bereits autoreleased. Daher darfst Du es nicht noch einmal mit einer -autorelease Message beglücken. Das Objekt wird dadurch doppelt freigegeben. Das Dein Programm deswegen nicht abstürzt ist reine Glückssache.

    Michael
  • um den thread hier mal nicht einschlafen zu lassen, hab ich mir ein neues problem
    "ausgedacht" :D

    ich bin immernoch in der rekursieven methode vom anfang.
    irgentwo da drin wird folgende methode aufgerufen:

    Quellcode

    1. - (NSArray*) imageResolution: (NSString*) path
    2. {
    3. NSImageRep * myImageRep = [NSImageRep imageRepWithContentsOfFile:path];
    4. NSString *w = [NSString stringWithFormat:@"%0.d",[myImageRep pixelsWide]];
    5. NSString *h = [NSString stringWithFormat:@"%0.d",[myImageRep pixelsHigh]];
    6. NSString *r = [NSString stringWithFormat:@"%0.lf",(([myImageRep pixelsHigh] * 72) / [myImageRep size].height)];
    7. NSString *s = [NSString stringWithFormat:@"%0.2f",([[[fileManager fileAttributesAtPath:path traverseLink:YES] objectForKey:NSFileSize] floatValue] / 1024 / 1024)];
    8. [myImageRep release];
    9. return [NSArray arrayWithObjects:w,h,r,s,nil];
    10. }


    diese liest die dimension und auflösung und dateigrösse jedes bildes.
    das problem ist die zeile 8. ohne dem release läuft der speicher voll da er jedes bild im speicher behällt und nicht wieder frei gibt.
    und das sind bei 80 GB bilddaten ne ganze menge an RAM. mit dem release wird zwar das bild immerwieder aus dem
    speicher geschmissen, aber jetzt stürzt das programm am ende der rekursiven methode ab. das eine ist genauso schlecht wie das andere.
    was kann man da machen?

    sascha