Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Libération mémoire

15 réponses
Avatar
moutonge
Bonjour,

Je voudrais v=E9rifier si la m=E9moire de mon programme =E9crit en C,
compil=E9 sous linux, et bien lib=E9r=E9e.
J'ai essay=E9 avec la command 'top' qui donne la taille en m=E9moire
VIRT, RES et SHR. Au fur =E0 =E0 mesure que des dossiers sont ouverts,
la taille de VIRT et RES augmente, mais au fur et =E0 mesure que les
dossiers sont ferm=E9s, la taille de RES reste =E0 la m=EAme valeur et ne
revient pas =E0 la taille initiale.
Ca fait la m=EAme chose avec l'=E9diteur 'vim', dont je suppose qu'il
fonctionne correctement, et ou la taille augmente au fur et =E0 mesure
de l'ouverture de fichiers, mais reste constante au fur et mesure de la
fermeture des fichiers.
Aussi, je me demande si cette m=E9thode est valable, sinon existe-il
d'autres m=E9thodes pour v=E9rifier la lib=E9ration de la m=E9moire?

Merci
G=E9rard Mouton

10 réponses

1 2
Avatar
Marc Boyer
Le 12-01-2007, a écrit :
Je voudrais vérifier si la mémoire de mon programme écrit en C,
compilé sous linux, et bien libérée.
J'ai essayé avec la command 'top' qui donne la taille en mémoire
VIRT, RES et SHR. Au fur à à mesure que des dossiers sont ouverts,
la taille de VIRT et RES augmente, mais au fur et à mesure que les
dossiers sont fermés, la taille de RES reste à la même valeur et ne
revient pas à la taille initiale.
Ca fait la même chose avec l'éditeur 'vim', dont je suppose qu'il
fonctionne correctement, et ou la taille augmente au fur et à mesure
de l'ouverture de fichiers, mais reste constante au fur et mesure de la
fermeture des fichiers.
Aussi, je me demande si cette méthode est valable,


Visiblement non.

sinon existe-il
d'autres méthodes pour vérifier la libération de la mémoire?


1) valgrind
2) man malloc -> MALLOC_CHECK

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)

Avatar
Vincent Lefevre
Bonjour,

Dans l'article ,
écrit:

Je voudrais vérifier si la mémoire de mon programme écrit en C,
compilé sous linux, et bien libérée.
J'ai essayé avec la command 'top' qui donne la taille en mémoire
VIRT, RES et SHR. Au fur à à mesure que des dossiers sont ouverts,
la taille de VIRT et RES augmente, mais au fur et à mesure que les
dossiers sont fermés, la taille de RES reste à la même valeur et ne
revient pas à la taille initiale.
Ca fait la même chose avec l'éditeur 'vim', dont je suppose qu'il
fonctionne correctement, et ou la taille augmente au fur et à mesure
de l'ouverture de fichiers, mais reste constante au fur et mesure de la
fermeture des fichiers.
Aussi, je me demande si cette méthode est valable, sinon existe-il
d'autres méthodes pour vérifier la libération de la mémoire?


Tout dépend ce que tu entends par "libération de la mémoire". Cf par
exemple le manuel de la glibc:

Occasionally, `free' can actually return memory to the operating
system and make the process smaller. Usually, all it can do is allow a
later call to `malloc' to reuse the space. In the meantime, the space
remains in your program as part of a free-list used internally by
`malloc'.

--
Vincent Lefèvre - Web: <http://www.vinc17.org/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/>
Work: CR INRIA - computer arithmetic / Arenaire project (LIP, ENS-Lyon)

Avatar
Jano
Vincent Lefevre wrote:


Tout dipend ce que tu entends par "libiration de la mimoire". Cf par
exemple le manuel de la glibc:

Occasionally, `free' can actually return memory to the operating
system and make the process smaller. Usually, all it can do is allow
a later call to `malloc' to reuse the space. In the meantime, the
space remains in your program as part of a free-list used internally
by `malloc'.


OUI..

En fait j'ai trouvé il y a longtemps (sur un site en Australie je
crois), l'explication simple de cela :

1) Quand on alloue de la mémoire, le programme augmente sa plage
résidente allouée par le loader, puisqu'il est en train de tourner.

2) Là 2 cas interviennent :

a) Ou on désalloue la mémoire allouée sans en avoir alloué
d'autre entre temps, auquel cas on peut faire redescendre la plage
allouée (mais ceci peut être empêché parce qu'on a appelé une routine
que le loader charge..)

b) Soit on a alloué de la mémoire entre temps (ou appelé une
routine (voir ci-dessus)). Dans ce cas il est quasi impossible de
redescendre (sauf (éventuellement) si on a fait appel à des librairies
dynamiques).



La raison de ce comportement est simple, en fait :


xxxxxxxaaaaaaaaaaaaaaaabbbbbbbbbuuuuuuuuuuuuuuu

Soit X la mémoire occupée au départ.. a celle qu'on alloue..

Si b est une routine chargée et uuu une zone mémoire allouée dans cette
routine, même si on la libère, bbb sera toujours chargé pendant 1
moment.. Donc free(a) rendra la portion "aa" disponible, mais ne
dépilera pas (bien sûr) bbb, et donc la taille ne diminuera pas..

Si par contre il n'y a pas d'autre allocation à la suite, et pas
d'appels ou de chargement dans le même segment de programme (on
s'arrête à la fin des aa), dans ce cas on peut revenir à xx..


En conclusion, si tu as de grands tableaux à réallouer au cours du
temps, cette solution (realloc) est à éviter car elle fait grossir en
permanence la mémoire. Par contre, une liste chaînée croit ou diminue
par UN élément, et donc (voir le trou généré ci-dessus) cet élément
peut souvent être alloué sans changer (ou peu) la mémoire globale...

Ce qui veut dire que les trous sont réutilisables pour des structures
de taille <= au trou..

Par exemple un programme qui régulèrement appellerait une routine

Print_Status ( int Status )
{
char *chaine=(char *)NULL ;

chaîne = (char *) calloc (80, sizeof(char) );
if ( chaîne != (char *)NULL )
{
sprintf ( chaine, "Status : %d",Status);
fprintf ( stderr, "%s" chaine); /* OUI JE SAIS, mais c'est pour la
démo :-) */
free(chaine);
}
}

aurait de forte chance de voir toujours le même segment de mémoire lui
être assigné à chaque allocation (un trou de 80 à combler..)


Donc pour vraiment vérifier si ta mémoire est libérée, il te faut
(suivant ce que fait ton programme), le faire tourner longtemps (en
tous cas moi c'est ce que je fais) pour voir si ça se stabilise, ou si
ça continue de monter... Mais ça dépend beaucoup du programme...

PS: un autre moyend de se convaincre de l'impossibilité de gérer ça est
de faire un petit test (je fais juste en pseudo code):

char *s1 ;
char *s2 ;
char *s3 ;

alloc (s1)
alloc (s2)
alloc (S3)

free(s2)

Quand le gestionnaire a alloué s3, il l'a placé à la fin de ce qui
était alloué en s2..
Si on libère s2, pour gagner de la place, il faudrait déplacer la plage
allouée à s3 à la fin de s1..

Mais comme le gestionnaire de mémoire ne SAIT pas (et ne pourra jamais
savoir) que TOI tu as mis la 3ième zone dans un paramètre s3, même si
il déplaçait la plage, il n'aurait AUCUN moyen de te renvoyer dans le
programme la nouvelle valeur du pointeur s3..

Avatar
Antoine Leca
Jano écrivit dans news:45bfe69e$0$25945$:
a) Ou on désalloue la mémoire allouée sans en avoir alloué
d'autre entre temps, auquel cas on peut faire redescendre la plage
allouée (mais ceci peut être empêché parce qu'on a appelé une routine
que le loader charge..)


Ou bien parce que la routine qui gère l'allocation *interne* à la
bibliothèque C (malloc et ses frères) préfère garder cette /réservation/
pour elle et ne la « rend » pas au système d'exploitation.

Avec les systèmes d'exploitation avec mémoire virtuelle, cette stratégie est
commune car elle simplifie grandement la programmation de malloc, tout en
ayant un impact limité (de l'ordre de 0,1%) sur la consommation réelle de
mémoire physique.


Par ailleurs, je ne comprend pas bien à quel type de « routine du chargeur »
tu fais allusion.


b) Soit on a alloué de la mémoire entre temps (ou appelé une
routine (voir ci-dessus)). Dans ce cas il est quasi impossible de
redescendre (sauf (éventuellement) si on a fait appel à des
librairies dynamiques).


Et encore, voire au contraire.
Si on utilise des bibliothèques dynamiques, et particulièrement si malloc ou
son équivalent est dans une bibliothèque potentiellement différente de celle
des clients, l'algorithme va utiliser une sécurité plus grande pour éviter
de « perdre » un espace d'adressage qu'il a réservé antérieurement, donc je
m'imagine qu'il y a encore moins de chances qu'il « rende » au système un
espace d'adressage virtuel suite à une désallocation.


En conclusion, si tu as de grands tableaux à réallouer au cours du
temps, cette solution (realloc) est à éviter car elle fait grossir en
permanence la mémoire.


Parfois. Oui en général, et de plus en plus car les algorithmes de malloc()
tendent à être optimisés pour la situation ci-dessous (listes) plutôt que
pour l'utilisation de realloc()... Les conséquences de la programmation dite
orientée à objets...

Par contre, une liste chaînée croit ou diminue
par UN élément, et donc (voir le trou généré ci-dessus) cet élément
peut souvent être alloué sans changer (ou peu) la mémoire globale...


Oui.

Ce qui veut dire que les trous sont réutilisables pour des structures
de taille <= au trou..


Pas exactement. C'était peut-être vrai avec les algorithmes simples de
malloc() d'il y a 35 ans (comme celui de Ken Thomson, dont SCO a accusé
Linux de plagiat en 2001), mais aujourd'hui il y a d'autres stratégies,
comme celles des listes de tailles 1<<N (qui existe depuis plus de 20 ans)
qui ont des comportements différents.

Et de toute manière, tous les algorithmes de malloc() non bogués savent
fusionner deux ou trois trous adjacents, et allouer si nécessaire l'espace
pour une structure de taille supérieure à chacun des trous mais inférieure à
leur somme.


Antoine

Avatar
Jano
Antoine Leca wrote:

Par ailleurs, je ne comprend pas bien à quel type de « routine du
chargeur » tu fais allusion.



Pour les très gros programmes, l'éxecutable n'est pas forcément chargé
en mémoire dans son entier... Si une routine figure dans la partie non
chargée, et qu'on vient à l'appeler, le loader va charger un tronçon du
binaire la contenant..


b) Soit on a alloué de la mémoire entre temps (ou appelé une
routine (voir ci-dessus)). Dans ce cas il est quasi impossible de
redescendre (sauf (éventuellement) si on a fait appel à des
librairies dynamiques).


Et encore, voire au contraire.
Si on utilise des bibliothèques dynamiques, et particulièrement si
malloc ou son équivalent est dans une bibliothèque potentiellement
différente de celle des clients, l'algorithme va utiliser une
sécurité plus grande pour éviter de « perdre » un espace d'adressage
qu'il a réservé antérieurement, donc je m'imagine qu'il y a encore
moins de chances qu'il « rende » au système un espace d'adressage
virtuel suite à une désallocation.



Pas évident du tout.. Regarde X et la toolkit. Le fait d'appeler
XtFree marche pafaitement, car Xt contrôle parfaitement son espace..

Ainsi avec xemacs chaque fois que tu charges un fichier la mémoire
croît , puis tu le décharges et la mémoire diminue d'autant..


Et de toute manière, tous les algorithmes de malloc() non bogués
savent fusionner deux ou trois trous adjacents, et allouer si
nécessaire l'espace pour une structure de taille supérieure à chacun
des trous mais inférieure à leur somme.


Absolument.. Mais je citais juste un point pour mieux comprendre :-)
Et d'ailleurs si ils sont adjacents oui, si ils ne le sont pas ça se
complique fortement...


Avatar
Harpo
Jano wrote:

Pas évident du tout.. Regarde X et la toolkit. Le fait d'appeler
XtFree marche pafaitement, car Xt contrôle parfaitement son espace..

Ainsi avec xemacs chaque fois que tu charges un fichier la mémoire
croît , puis tu le décharges et la mémoire diminue d'autant..


Ils doivent utiliser quelque chose comme 'memory mapped IO', ce que peut
utiliser malloc/free au-delà d'une certaine taille d'allocation, ce ne
peut être le cas lorsque la mémoire est utilisée par petits bouts de
quelques centaines d'unités.

Et de toute manière, tous les algorithmes de malloc() non bogués
savent fusionner deux ou trois trous adjacents, et allouer si
nécessaire l'espace pour une structure de taille supérieure à chacun
des trous mais inférieure à leur somme.


Absolument.. Mais je citais juste un point pour mieux comprendre :-)
Et d'ailleurs si ils sont adjacents oui, si ils ne le sont pas ça se
complique fortement...


Même s'ils sont adjacents, free ne va peut-être pas rabouter des
morceaux si ils sont sur des pages différentes au risque d'un page-in
de l'autre page et qu'elle soit marquée 'dirty'.

--
NOUVELLE proposition pour USENET-FR :
AAD1 : news:
BILAN1 : news:45bb70d0$0$2381$


Avatar
Antoine Leca
Jano écrivit dans news:45c093e7$0$25924$:
Antoine Leca wrote:

Par ailleurs, je ne comprend pas bien à quel type de « routine du
chargeur » tu fais allusion.


Pour les très gros programmes, l'éxecutable n'est pas forcément chargé
en mémoire dans son entier... Si une routine figure dans la partie non
chargée, et qu'on vient à l'appeler, le loader va charger un tronçon
du binaire la contenant..


Toujours pas compris.

Avec un système de mémoire virtuel (cas courant aujourd'hui, surtout pour
les « très gros programmes »), en fait le chargeur ne charge à peu près rien
du programme (par contre, il va charger pas mal de données et autres tables,
pour résoudre les relocations) ; et c'est le système de mémoire virtuelle
(gestionnaire de fautes de pages ou de segments) qui va charger, au fur et à
mesure des besoins, ce qui est nécessaire depuis la mémoire secondaire.
Idem pour les bibliothèques dynamiques (qui y sont fortement liées). Le seul
point ici, c'est l'allocation de l'espace d'adressage des bibliothèques
dynamiques. Dans les premiers schémas (SVR3, Linux libc5), les bibliothèques
n'étaient pas relogeables, et l'espace d'adressage était figé (justement
pour ne pas tomber dans le problème que tu évoques ci-dessus). Depuis, les
nouveaux schémas (ELF, PE) permettent de reloger, et on joue avec la
totalité de l'espace d'adressage (2 ou 3 Gio en 32 bits), en plaçant les
blocs (correspondant aux «binaires») là où il y a de la place, sans souci.


Maintenant, pour les très grosses _applications_, les développeurs peuvent
avoir découpé l'application en plusieurs programmes plus ou moins
indépendants, qui sont des processus différents, avec chacun leur gestion de
la mémoire dynamique du processus, et aussi éventuellement la gestion des
plages de mémoire partagées (shm, mmap, etc.) S'il y a alors un « chargeur »
qui est chargé de faire un peu d'arbitrage (surtout pour les partages),
c'est du cousu main...
Si le tout tourne sur un système sans mémoire virtuelle (ce qui était le cas
pour les applications DOS, par exemple) alors effectivement cela contraint
la gestion de la mémoire (qu'est ce que l'on a pu passer des heures à jouer
avec cela... il y a longtemps!) Mais depuis les années 1980 on dispose de
plateformes matérielles (VAX, 80286, etc.) ou logicielles (Windows 1.0,
peut-être OS/370 aussi) qui s'affranchissent de cette limite.


b) Soit on a alloué de la mémoire entre temps (ou appelé une
routine (voir ci-dessus)). Dans ce cas il est quasi impossible de
redescendre (sauf (éventuellement) si on a fait appel à des
librairies dynamiques).


Et encore, voire au contraire.
Si on utilise des bibliothèques dynamiques, et particulièrement si
malloc ou son équivalent est dans une bibliothèque potentiellement
différente de celle des clients, l'algorithme va utiliser une
sécurité plus grande pour éviter de « perdre » un espace d'adressage
qu'il a réservé antérieurement, donc je m'imagine qu'il y a encore
moins de chances qu'il « rende » au système un espace d'adressage
virtuel suite à une désallocation.


Pas évident du tout.. Regarde X et la toolkit.


Préalable : je ne connais pas cette implémentation.

Le fait d'appeler
XtFree marche pafaitement, car Xt contrôle parfaitement son espace..


Je ne vois pas bien le rapport, ni avec les bibliothèques dynamiques, ni
avec l'allocation réelle de la mémoire ; pour autant que je saches, Xt
utilise malloc()/free() pour toutes ses opérations, donc en fait on observe
le comportement du malloc() du système en question (y compris une éventuelle
bibliothèque interposée) ; et selon la bibliothèque d'allocation utilisée,
on aura un comportement ou un autre, et c'est justement de cette incertitude
dont je parlais ci-dessus.


Ainsi avec xemacs chaque fois que tu charges un fichier la mémoire
croît , puis tu le décharges et la mémoire diminue d'autant..


Je me demande quelle « mémoire » tu observes et comment. Si la mémoire était
réellement libérée, un second chargement subséquent du même fichier devrait
prendre exactement le même temps, est-ce le cas ?
De plus, a priori tu n'es pas dans ton cas b), mais bien plutôt dans le cas
a) (allocation séquentielle et libération dans l'ordre inverse, LIFO).


Antoine



Avatar
Harpo
Antoine Leca wrote:

Mais depuis
les années 1980 on dispose de plateformes matérielles (VAX, 80286,
etc.) ou logicielles (Windows 1.0, peut-être OS/370 aussi)


Non, IBM/370 c'était avant, dans la fin des 70' avec OS/MVS qui a du
commencer sur les machines IBM-168, il y avait un dispositif DAT (D
(ynamic?) Address Translation) correspondant +- au MMU (que je ne
rappelle pas non plus ce que veut dire le sigle). En plus avec un jeu
d'instructions extrémemnt simple, sauf 3 ou 4 instructions.

Je suis effrayé à l'idée de ne savoir où mettre un FU2.

--
NOUVELLE proposition pour USENET-FR :
AAD1 : news:
BILAN1 : news:45bb70d0$0$2381$

Avatar
Sylvain
Harpo wrote on 01/02/2007 20:40:

Non, IBM/370 c'était avant, dans la fin des 70' [...]
Je suis effrayé à l'idée de ne savoir où mettre un FU2.


un peu récent mais sûrement recevable chez fr.misc.bavardages.dinosaures

Sylvain.

Avatar
Antoine Leca
Harpo écrivit dans news:45c24218$0$32232$:
Antoine Leca wrote:

Mais depuis
les années 1980 on dispose de plateformes matérielles (VAX, 80286,
etc.) ou logicielles (Windows 1.0, peut-être OS/370 aussi)


Non, IBM/370 c'était avant,


Je sais. Mais je n'étais pas sûr que VM/370 (actuel z/VM) corresponde
réellement à mon propos, car je n'ai jamais travaillé avec ce bestiau.

dans la fin des 70' avec OS/MVS


Je pense que MVS (z/OS, *l'autre* OS des gros IBM) est un système
d'exploitation « classique », avec mémoire virtuelle matérielle.


Bon, si on revenait au 20e siècle ?


Antoine


1 2