[cocoa]Libération de la mémoire

  • Créateur du sujet Créateur du sujet Membre supprimé 2
  • Date de début Date de début
M

Membre supprimé 2

Invité
bonjour,

j'ai besoin d'effectuer une opération une bonne centaine de milliers de fois, une simple boucle "for" me suffirait.

malheureusement, j'ai un gros problème qui apparait lors des essayages: la libération de la mémoire des objets n'est pas immédiate et n'intervient qu'en fin de boucle, vu le nombre d'objets élevés créés, ça plante: MacOS X consomme toute sa mémoire puis sa mémoire virtuelle sur disque et paf!

Pour illustrer mes propos, voici un exemple tout bête:

for(i=0;i<1000000;i++)
{
myString = [[NSString alloc] initWithString:@"une phrase toute simple."];
[myString release];
}

avec l'outil ObjectAlloc, c'est assez flagrant qu'il va exister un millions d'objets NSString qui ne se détruiront qu'après la fin de la boucle.
et un "top" dans le Terminal pendant l'exécution inquiète sérieusement en voyant la mémoire libre descendre puis paginer à fond.

Contrairement à cet exemple, dans la boucle "for" de mon usage final, il n'y a pas bien entendu qu'un simple NSString, mais pleins d'autres objets, j'aimerais les détruire avant chaque réexécution de ma boucle, sous peine de n'avoir pas assez de mémoire pour le nombre de fois souhaité...

Toutes aides ou idées sont bienvenues.
 
Tu ne peux pas utiliser les méthodes de ces objets pour y mettre un autre contenu, ou juste les modifier un peu?

si les objets sont de classes que tu as créées toi-même, ajoute des méthodes pour faire ca...

avec dealloc, ca va pas mieux? (je ne sais pas si tu peux l'utiliser directement sur les objets)

a++
 
Alléger mon code ou recycler l'usage des objets utilisés ne fait que déplacer le problème selon moi et ça fait un peu bricolage.

Je pense lancer le code contenu dans ma boucle dans un Thread distinct, puisqu'ils possèdent leur propre mécanisme autorelease qui logiquement se purge en fin de thread...

Mais je voulais des avis avant de m'y lancer. Créer 20000 threads ou 20000 objets, telle est la question
wink.gif

en sachant que les threads mourront tous seuls, donc il ne devrait pas y en avoir autant.
 
Si a chaque passage de la boucle, tu n'utilise plus l'objet précédent, alors pourquoi le détruire et en créer un nouveau? Je trouve assez logique d'avoir une méthode qui en remplace le contenu, bien sûr, si c'est possible avec ta classe.

Pour les threads, je ne les utiliserais pas pour ca...
(je doute que le système accepte autant de threads)

tu peux aussi carrément revenir en arrière et faire des fonctions c...

je pense qu'il y a une autre manière beaucoup mieux pour faire ce que tu veux.
 
<BLOCKQUOTE><font size="1" face="Verdana, Geneva">quote:</font><HR>Posté à l'origine par peon.master:
Si a chaque passage de la boucle, tu n'utilise plus l'objet précédent, alors pourquoi le détruire et en créer un nouveau? Je trouve assez logique d'avoir une méthode qui en remplace le contenu, bien sûr, si c'est possible avec ta classe.

Ce pourrait etre une solution dans le cas de peu de code à l'intérieur de la boucle, mais ça se complique sitot que tu as des appels à quelques objets qui peuvent entrainer rapidement des memory leak comme ils disent, long à déterminer en plus.

Mais le plus genant restent les objets "mutable": toutes les array, dictionary etc sont à remettre à zéro: lent et compliqué pour suivre toutes les références release, pour les "immutable" je ne suis meme pas sur que tu puisse les remettre à zéro puisque par principe, ils sont pas très souples d'emploi
wink.gif


Pour les threads, je ne les utiliserais pas pour ca...
(je doute que le système accepte autant de threads)

Je commence à croire au contraire qu'il s'agit de la seule solution, le principal avantage étant qu'un thread se détruit entièrement après sa fin d'éxecution, entrainant ainsi la libération effective des objets utilisés en son sein.
Reste le problème de voir combien de threads va pouvoir supporter mon appli. Chaque thread devant récupérer une donnée par réseau, ils seront donc assez lent alors que ma boucle va les créer en nombre.
A essayer.

tu peux aussi carrément revenir en arrière et faire des fonctions c...
Et puis quoi encore
wink.gif
Quand tu vois l'économie de code d'un simple NSString initWithContentsOfURL
tongue.gif
faudrait etre fou pour reinventer la roue
smile.gif


<HR></BLOCKQUOTE>
 
<BLOCKQUOTE><font size="1" face="Verdana, Geneva">quote:</font><HR>Posté à l'origine par Natik:
http://projectomega.online.fr/contents/fr/php/oreilly_cocoa9.php?p=1

Voili Voila
smile.gif
grin.gif
grin.gif
<HR></BLOCKQUOTE>


Merci Natik, mais je reprécise que je n'ai pas de problème dans cette gestion de la mémoire des objets, je n'ai pas de fuite.
Mon seul souci, c'est que cette libération n'intervient qu'en fin de cycle, ce qui est tout à fait normal mais me pose problème quand une boucle avant la fin de cycle utilise un grand nombre d'objets qui s'accumulent.
 
Salut à tous,

Si, au lieu de mettre ton code dans la boucle for, tu faisait un appel à une méthode dans cette boucle, que dans cette méthode tu aloues tes objets; à la sortie de la méthode ils sont libérés non?

(for i=0; i&lt;1000000;i++) {
[self laMethode:leParamètre];
}

Mais malheureusement, je ne suis pas (encore) un expert au niveau Allocation et libération d'objets...

[03 juin 2002 : message édité par remi trom]
 
<BLOCKQUOTE><font size="1" face="Verdana, Geneva">quote:</font><HR>Posté à l'origine par remi trom:
Si, au lieu de mettre ton code dans la boucle for, tu faisait un appel à une méthode dans cette boucle, que dans cette méthode tu aloues tes objets; à la sortie de la méthode ils sont libérés non?<HR></BLOCKQUOTE>

Non
smile.gif


J'ai cru à cette logique moi aussi, et toutes les documentations lues laissaient planer le doute. Mais après des essais un peu barbare comme exécuter une boucle des millions de fois pour saturer la mémoire du Mac, ce qui est parfois très chiant à faire quand on a des Gigas de RAM installés et des disques très larges...
j'ai constaté que cette libération n'était rééllement effective qu'après la fin d'un passage dans la "run loop" principale. Les méthodes appelées pendant tous le process ne libèrent rien.

Les applis de surveillance comme ObjectAlloc ou MallocDebug ou OmniObjectMeter n'étaient pas très utiles parce que mon appli n'avait pas de "memory leak" et généralement ces applis plantent dès lors qu'on créé un peu trop d'objets.

Mais après avoir essayé les threads, je tiens semble t-il ma réponse. Pour reprendre ton code, ça donne:

(for i=0; i&lt;1000000;i++) {
[NSThread detachNewThreadSelector:@selector(laMethode)
toTarget:self
withObject:NULL];
}

ça marche impeccablement, l'appli va consommer de la mémoire puis va se stabiliser pour ne plus rien grignoter par la suite. Impeccable.
pour mon cas, je n'ai pas besoin de synchronisation, lock etc pour gérer mes threads, ils peuvent vivre leur vie comme bon leur semble, donc le code reste très très simple, ce detachNewThreadSelector suffit, et un NSAutoreleasePool* pool dans la méthode appelée et c'est tout.

La question que je pose maintenant aux pros du Cocoa est leur avis sur la meilleure façon d'utiliser detachNewThreadSelector. Dans mes essais, j'utilise une méthode qui est dans "self", ne vaudrait-il pas mieux qu'elle soit dans une classe distincte (risques d'imbrications pas très orthodoxe sinon) ?

Merci à tous pour votre participation passée... et future
wink.gif
 
J'allais justement te demander si en créant une nouvelle pool ça marcherait ... sans passer par la création d'un nouveau thread. De toute façon en créant un thread ça peut effectivement te permettre d'effectuer un travail en arrière plan qui serait assez lourd et bloquant autrement. Tant que t'as pas besoin de synchroniser tout ça c'est bien ...
 
Salut,
Tu sais PL, sous cocoa saches que tes objets et surtout leur durée de vie est lié au contexte dans lequel tu les crée. Dans ton cas c'est la boucle qui détermine leur durée de vie. Pour éviter le problème que tu crains, il faut forcer un contexte donc créer un pool local qui contiendra tes objets. Tu peux avant la fin de la boucle liberer ton pool ou si tes objets sont plus nombreux que grand, avoir une boucle interne pour ne les liberer que par groupe de n.

Voici un exemple donné par Apple dans sa doc.


void main()
{

NSArray *args = [[NSProcessInfo processInfo] arguments];
unsigned count, limit = [args count];
for (count = 1; count &lt; limit; count++){
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *fileContents;
NSString *fileName;

fileName = [args objectAtIndex:count];
fileContents = [[NSString alloc] initWithContentsOfFile:fileName];
[fileContents autorelease];

/* Process the file, creating and autoreleasing more objects. */

[pool release];
}

/* Do whatever cleanup is needed. */

exit (EXIT_SUCCESS);
}
 
Ah ben voilà qu'on progresse :-)

Je m'étais mis dans la tête qu'avec ce procédé, ça n'irait pas bien loin non plus, puisque je concluais que la release du pool interne à ma boucle ne serait vraiment relaché qu'une fois la boucle terminée, comme tous les autres release en fait.
Mais si tu me dis que ça marche autrement, tant mieux... je vais essayer ça.

Juste une question qui me tracasse avec ce NSAutoreleasePool, comment cocoa gère le fait de rattacher les objets autorelease à ce pool plutôt qu'à un autre?... Je m'explique un peu mieux, dans le code exemple que tu donnes:
[fileContents autorelease] ne cite pas explicitement l'objet NSAutoreleasePool pool que tu as créé pour la boucle... Qu'est-ce qui fait que cet autorelease sera géré par "pool" plutôt que celui de l'appli principale ou un autre pool qui pourrait existé pour ma classe, ou autres... ?

Mille mercis.
 
Eh oui mon cher PL si tu developpes plus en cocoa notamment quand tu utilisera les classe de drawing style NSBezierPath et même NSColor etc, tu sauras qu'ils utilisent la notion de contexte. Le contexte ici c'est le pool local.
En fait cela suit la même logique de programmation classique lorsque tu utilises dans une instruction une variable déclarée localement et une autre avec le même nom déclarée globalement.

A+

[03 juin 2002 : message édité par Manu]