Fuite mémoire ou boucle défaillante
cocoacola71
Membre
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.
Je suis perdu avec cette gestion de mémoire, j'ai compris pour les objets mais pour les entiers??
Merci de votre aide.
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:@"$"];<br />
<br />
NSString *cutMessage1 = [[NSString alloc]initWithString:[cutMessage objectAtIndex:0]];<br />
<br />
NSString *cutMessage2 = @"";<br />
if(![cutMessage1 isEqualToString:@""])<br />
cutMessage2 = [[NSString alloc]initWithString:[cutMessage objectAtIndex:1]];<br />
<br />
NSString *cutMessage3 = @"";<br />
if(![cutMessage2 isEqualToString:@""])<br />
cutMessage3 = [[NSString alloc]initWithString:[cutMessage objectAtIndex:2]];<br />
<br />
NSString *cutMessage4 = @"";<br />
if(![cutMessage3 isEqualToString:@""])<br />
cutMessage4 = [[NSString alloc]initWithString:[cutMessage objectAtIndex:3]];<br />
<br />
double h = 0;<br />
while (YES) {<br />
j++;<br />
h = 0;<br />
NSLog(@"nombre: %lu", (unsigned long)[affichage retainCount]);<br />
switch (j) {<br />
case 1:<br />
while (h<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<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<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<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.
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Donc non tu n'as pas compris pour les objets.
Le tout est dans un thread, pool est instancié tout au début de la méthode
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.
Avec une boucle en while (YES) on doit pas y passer souvent. /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 : 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.
Je t'ai donné la réponse...
Utilise GCD
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. /sad.png' class='bbc_emoticon' alt=':(' />
Je vais explorer GCD.
Avec GCD :
un truc du genre ...
J'ignore quelles sont les restrictions exactes sur le rafraà®chissement de l'IHM en dehors du thread principal.
- 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.
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
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).
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.
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).
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.
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...)
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.
Peut-être que s'il utilisait ARC il n'aurait pas une allocation qui explose ?
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.
En fermant les yeux, on voit beaucoup moins bien... /ph34r.png' class='bbc_emoticon' alt='' />
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 /smile.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.
Merci.
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.