Est-ce que les mises en œuvre malloc retour mémoire libre-ed au système?
Question
J'ai une application longue durée de vie avec une mémoire fréquente allocation désaffectation. Est-ce que tout retour de mise en œuvre de malloc mémoire libérée au système?
Qu'est-ce que, à cet égard, le comportement de:
- ptmalloc 1, 2 (par défaut glibc) ou 3
- dlmalloc
- tcmalloc (Google malloc fileté)
- solaris 10-11 par défaut malloc et mtmalloc
- FreeBSD 8 par défaut malloc (jemalloc)
- Hoard malloc?
Mise à jour
Si j'ai une application dont la consommation de mémoire peut être très différente pendant la journée et de la nuit (par exemple), puis-je forcer un retour à la mémoire libérée de malloc au système?
Sans ce retour de la mémoire libérée sera troqué et à plusieurs reprises, mais cette mémoire ne contient que des déchets.
La solution
L'analyse qui suit applique uniquement aux glibc (basé sur l'algorithme de ptmalloc2). Il y a certaines options qui semblent utiles pour retourner la mémoire libérée au système:
-
mallopt () (défini à l'
malloc.h
) ne fournit une option permettant de définir la valeur de seuil de garniture avec l'une de l'option de paramètreM_TRIM_THRESHOLD
, cela indique la quantité minimale de mémoire libre (en octets) a permis à la partie supérieure du segment de données. Si le montant est inférieur à ce seuil, glibc invoquebrk()
pour redonner la mémoire du noyau.La valeur par défaut de
M_TRIM_THRESHOLD
sous Linux est réglé sur 128K, réglage d'une valeur plus petite peut économiser de l'espace.Le même comportement pourrait être atteint par la mise en valeur de seuil équilibre dans l'environnement
MALLOC_TRIM_THRESHOLD_
variable sans source change absolument.Cependant, les programmes d'essais préliminaires exécutées à l'aide
M_TRIM_THRESHOLD
a montré que, même si la mémoire allouée par malloc ne retourne au système, la partie restante du morceau de mémoire réelle (l'arène) initialement demandé par l'intermédiairebrk()
a tendance à retenir. -
Il est possible de couper l'arène de la mémoire et de donner toute la mémoire inutilisée au système en appelant
malloc_trim(pad)
(défini dansmalloc.h
). Cette fonction redimensionne le segment de données, en laissant au moinspad
octets à la fin de celui-ci et, à défaut, si moins d'une valeur de la page d'octets peut être libéré. la taille du segment est toujours un multiple d'une page, qui est 4096 octets sur i386.La mise en œuvre de ce comportement modifié de
free()
en utilisantmalloc_trim
pourrait être fait en utilisant la fonctionnalité de crochet de malloc. Cela ne nécessiterait aucune modification de code source à la bibliothèque glibc de base. -
en utilisant l'appel système
madvise()
dans la mise en œuvre sans glibc.
Autres conseils
La plupart des implémentations ne prennent pas la peine d'identifier les (relativement rares) cas où « blocs » entiers (quelle que soit la taille convient le système d'exploitation) ont été libérés et pourraient être renvoyés, mais il y a bien sûr des exceptions. Par exemple, et je cite la page wikipedia , dans OpenBSD:
Sur un appel à
free
, la mémoire est libérée et unmapped de l'adresse de processus l'espace en utilisant munmap. Ce système est conçu pour améliorer la sécurité en prenant avantage de la mise en page de l'espace d'adressage Randomisation et caractéristiques page gap mis en œuvre dans le cadre dummap
OpenBSD appel système, et de détecter use-after-free-insectes comme une grande mémoire l'allocation est complètement unmapped après qu'il est libéré, en outre utiliser des causes une erreur de segmentation et de terminaison du programme.
La plupart des systèmes ne sont pas aussi la sécurité axée comme OpenBSD, cependant.
Sachant cela, quand je suis un système de codage de longue durée qui a une exigence connue à être transitoire pour une grande quantité de mémoire, j'essaie toujours de fork
le processus: le parent puis attend que les résultats de l'enfant [[typiquement sur une canalisation]], l'enfant fait le calcul (y compris l'allocation de mémoire), renvoie les résultats [[sur ladite canalisation]], puis se termine. De cette façon, mon processus de longue durée ne sera pas monopoliser la mémoire pendant les inutilement longues périodes entre les « pointes » occasionnels dans sa demande de mémoire. D'autres stratégies alternatives comprennent le passage à un allocateur de mémoire personnalisée pour les exigences particulières (C ++, il est relativement facile, si les langues avec des machines virtuelles en dessous telles que Java et Python en général ne le font pas).
Je traite le même problème que l'OP. Ainsi, il semble bien possible avec tcmalloc. J'ai trouvé deux solutions:
-
compiler votre programme avec tcmalloc lié, puis lancez comme:
env TCMALLOC_RELEASE=100 ./my_pthread_soft
la documentation mentionne que
Les taux raisonnables sont dans l'intervalle [0,10].
mais 10 ne semble pas assez pour moi (i.e. je ne vois pas de changement).
-
trouver quelque part dans votre code où il serait intéressant de libérer toute la mémoire libérée, puis ajoutez ce code:
#include "google/malloc_extension_c.h" // C include #include "google/malloc_extension.h" // C++ include /* ... */ MallocExtension_ReleaseFreeMemory();
La deuxième solution a été très efficace dans mon cas; le premier serait génial, mais ce n'est pas un grand succès, il est compliqué de trouver le bon numéro, par exemple.
J'ai eu un problème similaire dans mon application, après une enquête, j'ai remarqué que, pour une raison glibc ne retourne pas la mémoire au système lorsque les objets alloués sont de petite taille (dans mon cas moins de 120 octets).
Regardez ce code:
#include <list>
#include <malloc.h>
template<size_t s> class x{char x[s];};
int main(int argc,char** argv){
typedef x<100> X;
std::list<X> lx;
for(size_t i = 0; i < 500000;++i){
lx.push_back(X());
}
lx.clear();
malloc_stats();
return 0;
}
Sortie du programme:
Arena 0:
system bytes = 64069632
in use bytes = 0
Total (incl. mmap):
system bytes = 64069632
in use bytes = 0
max mmap regions = 0
max mmap bytes = 0
environ 64 Mo ne sont pas de retour au système. Quand j'ai changé typedef à:
sortie du programme typedef x<110> X;
ressemble à ceci:
Arena 0:
system bytes = 135168
in use bytes = 0
Total (incl. mmap):
system bytes = 135168
in use bytes = 0
max mmap regions = 0
max mmap bytes = 0
presque toute la mémoire a été libéré. J'ai aussi remarqué que l'utilisation malloc_trim(0)
dans les deux cas la mémoire libérée au système.
Voici la sortie après avoir ajouté malloc_trim
au code ci-dessus:
Arena 0:
system bytes = 4096
in use bytes = 0
Total (incl. mmap):
system bytes = 4096
in use bytes = 0
max mmap regions = 0
max mmap bytes = 0
Pour tous mallocs « normaux », y compris ceux que vous avez mentionnés, la mémoire est libérée pour être réutilisée par votre processus, mais pas au système entier. Libérer au système tout ne se produit que lorsque vous traitez est finalement terminé.
ceux que vous la liste, ne Hoard retournera la mémoire au système ... mais si elle peut effectivement faire cela dépendra beaucoup sur le comportement de l'allocation de votre programme.
La réponse courte: Pour forcer le sous-système malloc pour revenir à la mémoire OS, utilisez malloc_trim (). Dans le cas contraire, le comportement de la mémoire de retour dépend de l'implémentation.
Le malloc(3)
de FreeBSD 12 utilise jemalloc 5.1, qui renvoie la mémoire libérée ( "pages sales") à l'aide du système d'exploitation madvise(...MADV_FREE)
.
mémoire libérée n'est retourné après un retard de temps commandé par opt.dirty_decay_ms
et opt.muzzy_decay_ms
; voir le page de manuel et ce question sur la mise en œuvre purge page sale utilisé à base de décroissance pour plus de détails.
Les versions antérieures de FreeBSD livré avec les anciennes versions de jemalloc, qui renvoie également libéré la mémoire, mais utilise un algorithme différent pour décider quoi et quand purge.