Fuite mémoire ou boucle défaillante

cocoacola71cocoacola71 Membre
mars 2012 modifié dans API AppKit #1
Bonjour,



Un problème que je n'ai pas l'habitude de traité viens me faire face.

La boucle suivante affiche des données dans un NSTextField, celle ci se trouve dans un thread.

La boucle fonctionne environ 2 min puis fait planter l'application, j'ai donc pensé à  une fuite mémoire.

J'ai profilé l'appli avec Allocation et Zombie, le résultat "all alocation" augmente fortement lorsque while démarre puis à  chaque boucle all alocation augmente fortement jusqu'à  bloquer mon appli.


<br />
	    NSArray *cutMessage = [defilement componentsSeparatedByString:@&quot;&#036;&quot;];<br />
	     <br />
	    NSString *cutMessage1 = [[NSString alloc]initWithString:[cutMessage objectAtIndex:0]];<br />
	   <br />
	   NSString *cutMessage2 = @&quot;&quot;;<br />
	   if(&#33;[cutMessage1 isEqualToString:@&quot;&quot;])<br />
		   cutMessage2 = [[NSString alloc]initWithString:[cutMessage objectAtIndex:1]];<br />
	   <br />
	   NSString *cutMessage3 = @&quot;&quot;;<br />
	   if(&#33;[cutMessage2 isEqualToString:@&quot;&quot;])<br />
		   cutMessage3 = [[NSString alloc]initWithString:[cutMessage objectAtIndex:2]];<br />
	   <br />
	   NSString *cutMessage4 = @&quot;&quot;;<br />
	   if(&#33;[cutMessage3 isEqualToString:@&quot;&quot;])<br />
		   cutMessage4 = [[NSString alloc]initWithString:[cutMessage objectAtIndex:3]];<br />
<br />
double h = 0;<br />
		while (YES) {<br />
			j++;<br />
			h = 0;<br />
			NSLog(@&quot;nombre: %lu&quot;, (unsigned long)[affichage retainCount]);<br />
			switch (j) {<br />
				case 1:<br />
					while (h&lt;13) {<br />
						[affichage setFont:[NSFont systemFontOfSize:h]];<br />
						h = h+0.05;<br />
						[NSThread sleepForTimeInterval:0.001];<br />
						[affichage setStringValue:cutMessage1];<br />
					}<br />
					[NSThread sleepForTimeInterval:4.0];<br />
					//[affichage autorelease];<br />
					break;<br />
				case 2:<br />
					while (h&lt;13) {<br />
						[affichage setFont:[NSFont systemFontOfSize:h]];<br />
						h = h+0.05;<br />
						[NSThread sleepForTimeInterval:0.001];<br />
						[affichage setStringValue:cutMessage2];<br />
					}<br />
					[NSThread sleepForTimeInterval:4.0];<br />
				   // [affichage autorelease];<br />
					break;<br />
				case 3:<br />
					while (h&lt;13) {<br />
						[affichage setFont:[NSFont systemFontOfSize:h]];<br />
						h = h+0.05;<br />
						[NSThread sleepForTimeInterval:0.001];<br />
						[affichage setStringValue:cutMessage3];<br />
					}<br />
					[NSThread sleepForTimeInterval:4.0];<br />
				   // [affichage autorelease];<br />
					break;<br />
				case 4:<br />
					while (h&lt;13) {<br />
						[affichage setFont:[NSFont systemFontOfSize:h]];<br />
						h = h+0.05;<br />
						[NSThread sleepForTimeInterval:0.005];<br />
						[affichage setStringValue:cutMessage4];<br />
					}<br />
					[NSThread sleepForTimeInterval:4.0];<br />
					break;<br />
				default:<br />
				   break;<br />
			}<br />
			if(j==i)<br />
				j=0;<br />
		}<br />




Je suis perdu avec cette gestion de mémoire, j'ai compris pour les objets mais pour les entiers??



Merci de votre aide.
«1

Réponses

  • CeetixCeetix Membre
    mars 2012 modifié #2
    Moi tout ce que je vosi comme ça c'est que tu fais du alloc + init mais je vois pas un seul release. Nul part ...

    Donc non tu n'as pas compris pour les objets.
  • Il utilise peut-être ARC (qui est aussi disponible sur OS X)
  • cocoacola71cocoacola71 Membre
    mars 2012 modifié #4
    Ils se trouvent après ma boucle pour les NSString *cutMessage(1,2,3,4), je ne pense pas que je dois les inclure dans la boucle? image/huh.gif' class='bbc_emoticon' alt='???' />
    <br />
    		 [cutMessage1 release];<br />
    		 [cutMessage2 release];<br />
    		 [cutMessage3 release];<br />
    		 [cutMessage4 release];<br />
             [pool autorelease];<br />
    }<br />
    




    Le tout est dans un thread, pool est instancié tout au début de la méthode
  • CéroceCéroce Membre, Modérateur
    'cocoacola71' a écrit:


    Je suis perdu avec cette gestion de mémoire, j'ai compris pour les objets mais pour les entiers??


    Renseigne-toi sur l'allocation dynamique de la mémoire en langage C (fonctions malloc() et free() ).

    Tu comprendras que ta question n'a aucun sens, les types C étant en général alloués sur la pile.
  • MalaMala Membre, Modérateur
    'cocoacola71' a écrit:


    Ils se trouvent après ma boucle pour les NSString *cutMessage(1,2,3,4), je ne pense pas que je dois les inclure dans la boucle? image/huh.gif' class='bbc_emoticon' alt='???' />
    <br />
    		 [cutMessage1 release];<br />
    		 [cutMessage2 release];<br />
    		 [cutMessage3 release];<br />
    		 [cutMessage4 release];<br />
    }<br />
    



    Avec une boucle en while (YES) on doit pas y passer souvent. image/wink.png' class='bbc_emoticon' alt=';)' />



    Plus sérieusement, il te faut un NSAutoreleasePool dans ta boucle sinon tes objets autorelease ne seront jamais libérés.
  • @Idesroziers va falloir que je m'y fasse à  ça fuck. Il faudrait mettre une case avant de poster un nouveau topic. "Utilisation de ARC"
  • Merci de vos reponses,

    @ Idesroziers : non je n'utilise pas ARC.

    @ Ceroce : En c sous window, je n'ai jamais eu ce genre de problème. J'avoue que ma question n'a aucun sens, mais je ne comprend pas pourquoi cette maudite boucle bloque.
  • MalaMala Membre, Modérateur
    'cocoacola71' a écrit:
    @ Ceroce : En c sous window, je n'ai jamais eu ce genre de problème. J'avoue que ma question n'a aucun sens, mais je ne comprend pas pourquoi cette maudite boucle bloque.


    Je t'ai donné la réponse...
  • CéroceCéroce Membre, Modérateur
    Et sinon, y'a pas un truc qui dit qu'on n'a pas le droit de modifier l'IHM en dehors du thread principal... image/rolleyes.gif' class='bbc_emoticon' alt='::)' />
  • Ah bah oui j'avias pas vu, tu es dans un autre thread que le main thread ... Ne jamais modifier l'UI dans un autre thread que le main thread.

    Utilise GCD
  • @Mala: Oui j'écrivais en même temps image/smile.png' class='bbc_emoticon' alt=':)' />

    J'ai inclus NSAutoreleasePool, ma boucle semble fonctionner (9 min mais je pars de all Alocations : 20 MB à  91.41MB)

    Je ne pense pas que ceci soit normal. image/sad.png' class='bbc_emoticon' alt=':(' />
  • @Ceroce et @Ceetix : image/huh.gif' class='bbc_emoticon' alt='???' />?? cela engendrerai mes erreurs?? Pourtant d'autre thread gère des animations sur l'IHM!

    Je vais explorer GCD.
  • CeetixCeetix Membre
    mars 2012 modifié #14
    Oui c'est plantogène de faire de cette manière.

    Avec GCD :


    <br />
    <br />
    [color=#04afc8][color=#ffffff]	[/color]dispatch_queue_t[color=#ffffff] queue = [/color]dispatch_queue_create[color=#ffffff]([/color][color=#e2454c]&quot;queueWhile&quot;[/color][color=#ffffff], [/color][color=#c0399a]NULL[/color][color=#ffffff]);[/color][/color]<br />
    [color=#ffffff]	[color=#04afc8]dispatch_async[/color](queue, ^{[/color]<br />
    [color=#ffffff]		[color=#c0399a]double[/color] h = [color=#8b86cd]0[/color];[/color]<br />
    [color=#ffffff]		[color=#c0399a]int[/color] j = [color=#8b86cd]0[/color];[/color]<br />
    [color=#ffffff]		[color=#c0399a]while[/color] ([color=#c0399a]YES[/color]) {[/color]<br />
    [color=#ffffff]			j++;[/color]<br />
    [color=#ffffff]			h = [color=#8b86cd]0[/color];[/color]<br />
    [color=#ffffff]			NSLog([color=#e2454c]@&quot;nombre: %lu&quot;[/color], ([color=#c0399a]unsigned[/color] [color=#c0399a]long[/color])[affichage retainCount]);[/color]<br />
    [color=#ffffff]			[color=#c0399a]switch[/color] (j) {[/color]<br />
    [color=#ffffff]				[color=#c0399a]case[/color] [color=#8b86cd]1[/color]:[/color]<br />
    [color=#ffffff]					[color=#c0399a]while[/color] (h&lt;[color=#8b86cd]13[/color]) {[/color]<br />
    [color=#ffffff]						[color=#04afc8]dispatch_async[/color]([color=#d08f5e]dispatch_get_main_queue[/color](), ^{[/color]<br />
    [color=#ffffff]							[affichage setFont:[NSFont systemFontOfSize:h]];[/color]<br />
    [color=#ffffff]							[affichage setStringValue:cutMessage1];});[/color]<br />
    [color=#ffffff]						h = h+[color=#8b86cd]0.05[/color];[/color]<br />
    [color=#ffffff]						[[color=#04afc8]NSThread[/color] [color=#04afc8]sleepForTimeInterval[/color]:[color=#8b86cd]0.001[/color]];  [/color]<br />
    [color=#ffffff]					}[/color]<br />
    [color=#04afc8][color=#ffffff]					[[/color]NSThread[color=#ffffff] [/color]sleepForTimeInterval[color=#ffffff]:[/color][color=#8b86cd]4.0[/color][color=#ffffff]];[/color][/color]<br />
    [color=#ffffff]					[color=#c0399a]break[/color];[/color]<br />
    [color=#ffffff]				[color=#c0399a]default[/color]:[/color]<br />
    [color=#ffffff]					[color=#c0399a]break[/color];[/color]<br />
    [color=#ffffff]			}[/color]<br />
    [color=#ffffff]			[color=#c0399a]if[/color](j==i)[/color]<br />
    [color=#ffffff]				j=[color=#8b86cd]0[/color];[/color]<br />
    [color=#ffffff]		}[/color]<br />
    [color=#ffffff]	});[/color]<br />
    [color=#04afc8][color=#ffffff]	[/color]dispatch_release[color=#ffffff](queue);[/color][/color]<br />
    [color=#ffffff]}[/color]<br />
    <br />
    




    un truc du genre ...
  • CéroceCéroce Membre, Modérateur
    @ceetix: En quoi utiliser GCD change le problème ? Ton code met toujours à  jour l'IHM dans un thread secondaire, non ?

    J'ignore quelles sont les restrictions exactes sur le rafraà®chissement de l'IHM en dehors du thread principal.
  • CeetixCeetix Membre
    mars 2012 modifié #16
    Non mais GCD ou pas il faut updater dans le main thread ça c'est clair. Après moi j'aime bien utiliser GCD. Et là  si tu lis bien je mets à  jour son affichage en faisant du :
    [color=#000000] [/color][color=#04AFC8][color=#000000]dispatch_async[/color][/color][color=#666600]([/color][color=#D08F5E][color=#000000]dispatch_get_main_queue[/color][/color][color=#666600](),[/color][color=#000000] ...[/color]
    
  • Merci Ceetix, mais je pense qu'il me faut quelques explications:

    - J'ai lu sur le net, mais je ne comprend pas vraiment à  quoi sert GCD, (en tout cas son utilisation est flagrante, je n'utilise que 12MB au bout de 10 min d'utilisation contrairement à  90MB sans GCD)

    - J'ai lu qu'il était présent depuis snow leopard, donc l'utilisation pour les OS inférieur sera problématique?



    En tout cas merci.
  • zoczoc Membre
    'Céroce' a écrit:


    @ceetix: En quoi utiliser GCD change le problème ? Ton code met toujours à  jour l'IHM dans un thread secondaire, non ?

    J'ignore quelles sont les restrictions exactes sur le rafraà®chissement de l'IHM en dehors du thread principal.


    Bah non, clairement le code de Ceetix met à  jour l'IHM dans le thread principal. "dispatch_async(dispatch_get_main_queue(), ..." est grosso modo l'équivalent de performSelectorOnMainThread.



    Et je confirme qu'il est fortement recommandé par Apple de n'interagir avec l'IHM que depuis le thread principal de l'application: Un certain nombre de classes ne sont en effet pas thread safe, et en tout cas, c'est absolument le cas pour NSView, NSCell et leurs descendants: Threading Programming Guide
  • CéroceCéroce Membre, Modérateur
    mars 2012 modifié #19
    'cocoacola71' a écrit:


    - J'ai lu sur le net, mais je ne comprend pas vraiment à  quoi sert GCD, (en tout cas son utilisation est flagrante, je n'utilise que 12MB au bout de 10 min d'utilisation contrairement à  90MB sans GCD)


    Il a plusieurs rôle, mais il y en a un qui nous intéresse particulièrement. Admettons qu'une tâche puisse être découpée en de nombreuses sous-tâches. Dans ton exemple, on pourrait créer une sous-tâche par TextField, puisqu'elles sont indépendantes. Mais dans les faits, multiplier les threads a un impact négatif sur les performances, alors qu'au mieux, sur un processeur bi-coe“ur, deux threads peuvent s'exécuter simultanément. GCD gère ces détails lui-même.



    Il faut aussi le voir comme un moyen plus simple de créer des threads et de les synchroniser, puisqu'il gère les notions de dépendance ou d'indépendance des tâches.



    En Cocoa, tu disposes d'une couche d'abstraction supplémentaire, à  savoir NSOperation et NSOperationQueue qui font que tu n'as même pas besoin de connaà®tre l'API en C de GCD (alias libdispatch).


    'cocoacola71' a écrit:


    - J'ai lu qu'il était présent depuis snow leopard, donc l'utilisation pour les OS inférieur sera problématique?


    Oui, mais veux-tu vraiment gérer les systèmes 10.5 voire antérieurs, sachant qu'ils représentent vraiment une minorité d'utilisateurs aujourd'hui, et qu'il faut 10.6.6 minimum pour le Mac App Store ?



    Si tu savais comme je galère pour que mon appli fonctionne encore sous 10.5; je suis obligé de compiler une version spéciale avec Xcode 3.2.5. On attend Mountain Lion pour cet été, je peux te dire que quand 10.8 sortira, c'en sera fini de 10.5 pour moi. Tester et garantir la compatibilité sur 3 versions de l'OS est déjà  trop de boulot.
  • MalaMala Membre, Modérateur
    GCD ou pas GCD, le problème ici vient du fait que les accès IHM d'OSX ne sont absolument pas Thread Safe. Là  il suffit de remplacer les dispatch_async par des - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait et c'est réglé.
  • zoczoc Membre
    'cocoacola71' a écrit:
    je ne comprend pas vraiment à  quoi sert GCD


    Grand Central Dispatch, grosso modo, c'est une librairie au dessus des threads posix. Son rôle principal consiste à  gérer les threads à  la place du développeur, en proposant une abstraction de plus haut niveau: Les files (queues) de tâches.



    Les tâches soumises sur une file peuvent être exécutées de manière synchrone ou asynchrone, sur le thread principal (cas d'utilisation de la file retournée par l'appel à  dispatch_get_main_queue) ou sur des threads secondaires dont tu n'as pas à  t'occuper, GCD gérant tout seul leur cycle de vie, leur priorité, etc...



    GCD permet également l'exécution de plusieurs tâches en parallèles avec une possibilité d'enchainement et de synchronisation de tâches par rendez-vous (par exemple, GCD peut exécuter les tâches 1 et 2 en parallèle, puis enchainer l'exécution de la tâche 3 lorsque 1 et 2 sont terminées).



    Bref, GCD peut remplacer efficacement 99% des utilisations de NSThread ou des threads posix. Et en plus, étant basé sur les blocks, sont utilisation est simple et élégante (lol on dirait une phrase de marketeux).
  • Merci à  vous pour vos explications.

    Cela me semble beaucoup plus clair maintenant.

    Et je pense que m'on application sera ravie de ces changements.

    Je vais pouvoir revenir sur certaines choses qui bloquaient à  l'utilisation de NSThread.
  • AliGatorAliGator Membre, Modérateur
    mars 2012 modifié #23
    Le fait de ne mettre à  jour l'UI d'une application uniquement dans le Main Thread n'est d'ailleurs pas forcément propre à  Cocoa : c'est très courant dans beaucoup de SDKs.

    La mise à  jour de l'interface graphique d'une application étant gérée par la main loop, qui se charge de faire toute la partie composition, rendu, et display (c'est assez transparent pour le développeur final du coup qui n'a qu'à  implémenter des méthodes drawRect ou modifier les propriétés des NSControls/UIControls pour que tout se fasse tout seul)



    Rares sont les SDK qui permettent de modifier l'IHM dans un thread secondaire (j'ai eu le mm souci en C# sous Windows par exemple, ou en C++ avec QT)



    Une fois que tu auras corrigé ça -- qui est la raison principale du plantage que tu as -- typiquement en utilisant GCD (ce n'est pas la seule méthode il y a d'autres alternatives, mais en l'état depuis que GCD est dispo ça facilite bcp les choses en terme de gestion de multithreading), il y aura ensuite des fuites mémoire à  corriger, qui ne sont pas plantogènes mais vont faire grossir démesurément ton empreinte mémoire (objets créés non désalloués)



    D'ailleurs quitte à  utiliser GCD, tu pourrais limite abandonner les NSThread pour ton traitement et le faire dans une queue GCD aussi. Ca te simplifiera les choses et t'évitera des déconvenues avec toutes les subtilités à  gérer quand on utilise un thread (deadlocks, autoreleasepools, communication inter threads, protection de ressources...)
  • Par contre j'ai une dernière question,

    lorsque je profile avec Allocations mon appli, il m'arrive d'avoir l'erreur suivante:

    "objc_msgsend_vtable12" qui correspond à  isEqual selon le net.

    Or je n'ai pas de isEqual!

    Ou bien cette erreur "objc_msgsend".

    Cela arrive lorsque l'appli tourne depuis 4 ou 5 min.

    Seul ma boucle tourne encore...



    Je ne comprend pas de quoi cette erreur survient..

    Si quelqu'un a une idée.
  • 'ldesroziers' a écrit:


    Il utilise peut-être ARC (qui est aussi disponible sur OS X)




    Peut-être que s'il utilisait ARC il n'aurait pas une allocation qui explose ?
  • KubernanKubernan Membre
    mars 2012 modifié #26
    'cocoacola71' a écrit:


    Par contre j'ai une dernière question,

    lorsque je profile avec Allocations mon appli, il m'arrive d'avoir l'erreur suivante:

    "objc_msgsend_vtable12" qui correspond à  isEqual selon le net.

    Or je n'ai pas de isEqual!

    Ou bien cette erreur "objc_msgsend".

    Cela arrive lorsque l'appli tourne depuis 4 ou 5 min.

    Seul ma boucle tourne encore...



    Je ne comprend pas de quoi cette erreur survient..

    Si quelqu'un a une idée.




    Quand tu fait appel à  une méthode, celle ci fait appel elle même à  d'autres méthodes que tu n'as pas écrites.

    Si par exemple tu créé un sort descriptor sur un string, à  un moment donné le runtime va appeler [NSString compare:...] sans que tu ai besoin de l'écrire.



    Faut remonter le crash et voir quelle est la méthode de ton programme qui est à  l'origine de l'erreur.
  • MalaMala Membre, Modérateur
    'Kubernan' a écrit:


    Peut-être que s'il utilisait ARC il n'aurait pas une allocation qui explose ?


    En fermant les yeux, on voit beaucoup moins bien... image/ph34r.png' class='bbc_emoticon' alt='' />
  • @Aligator, merci de ton explication.

    Je pense que oui je vais abandonner les NSThread , et essayer de résoudre le reste des fuites mémoires.

    J'ai encore du boulot image/smile.png' class='bbc_emoticon' alt=':)' />
  • 'Mala' a écrit:


    En fermant les yeux, on voit beaucoup moins bien... image/ph34r.png' class='bbc_emoticon' alt='' />




    Jme doute, je ne parlais pas d'une solution de remplacement, mais juste que par déduction on pouvait penser qu'il n'utilisait pas ARC et que donc ça manquait de release.
  • @Kubernan, je vais fouiner un peu pour éviter ce crash.

    Merci.
  • Chaque objet a droit à  son release, tout les objet initier dans mon prog.

    Mais quelques fois, comme les initiation avant ma boucle, tant que celle-ci n'est pas terminée ils ne seront pas alloués.

    Je pense que ce problème joue bcp.
Connectez-vous ou Inscrivez-vous pour répondre.