OVH Cloud OVH Cloud

Allouer plus de mémoire que disponible

76 réponses
Avatar
Rémi Moyen
Salut,

J'ai une question sur la mani=E8re dont se comporte un programme sous
Linux, en C++, quand on essaye d'allouer plus de m=E9moire que
disponible sur le syst=E8me. =C0 la base, l'op=E9rateur new lance dans ce
cas-l=E0 une exception et c'est =E0 moi de la rattraper et de la traiter,
tr=E8s bien.

Mais mon probl=E8me est de savoir si, par derri=E8re, le noyau essaye
d'allouer toute la m=E9moire avant de se rendre compte qu'il n'en a pas
assez de disponible, ou est-ce qu'il fait d'abord une v=E9rification
avant de se lancer l=E0-dedans ?

Dans le cas o=F9 la m=E9moire est sur de la RAM, =E7a ne change probablement
pas grand chose (en perception par l'utilisateur), mais si il y a du
swap, est-ce que le noyau va faire swapper tout ce qu'il peut pendant
une heure avant de se rendre compte qu'il lui manque 42 octets ?

Si c'est le cas, alors j'imagine que j'ai int=E9r=EAt =E0 impl=E9menter
quelques checks rapides sur la m=E9moire disponible avant de commencer =E0
allouer des blocs de 1 Go (=E7a m'arrive...), sinon mes utilisateurs
risquent de ne pas trop aimer devoir attendre longtemps, et swapper
tout les autres programmes, avant de s'entendre dire qu'il n'y a pas
assez de m=E9moire pour faire ce qu'ils veulent ! Et dans ce cas-l=E0
encore, y'a-t-il une mani=E8re plus ou moins "standard" de v=E9rifier "=E0
la main" la m=E9moire disponible ?

Et accessoirement, j'imagine que diff=E9rents OS peuvent se comporter
diff=E9remment de ce point de vue, donc est-ce pareil pour SunOS ?
IRIX ? (les deux autres syst=E8mes que je suis susceptible d'utiliser)

Merci d'avance !
--
R=E9mi Moyen

10 réponses

1 2 3 4 5
Avatar
talon
Rémi Moyen wrote:
Salut,

J'ai une question sur la manière dont se comporte un programme sous
Linux, en C++, quand on essaye d'allouer plus de mémoire que
disponible sur le système. À la base, l'opérateur new lance dans ce
cas-là une exception et c'est à moi de la rattraper et de la traiter,
très bien.

Mais mon problème est de savoir si, par derrière, le noyau essaye
d'allouer toute la mémoire avant de se rendre compte qu'il n'en a pas
assez de disponible, ou est-ce qu'il fait d'abord une vérification
avant de se lancer là-dedans ?


C'est le fameux problème de l'"overcommit". Probablement tous les
systèmes Unix modernes, et certainement Linux et FreeBSD font comme les
banquiers qui prêtent de l'argent qu'ils n'ont pas, ils allouent de la
mémoire qui n'est pas disponible, et on voit *aprés* si dès fois il y a
un problème. Il paraît que faire comme ça est indispensable pour
préserver les performances. La conclusion est que si on veut s'assurer
d'avoir rééllement la mémoire disponible, il faut aller écrire dans
chaque page en trappant le signal SEGV. Autrement dit, ça doit
probablement dire que ce qu'on attend de l'opérateur new en C++ ne
fonctionne pas du tout. Il n'y a aucun garantie que la mémoire sera
rééllement disponible sans effectuer des opérations de plus bas niveau
que celles que le C++ fournit en standard.


--

Michel TALON

Avatar
talon
Rémi Moyen wrote:
Salut,

J'ai une question sur la manière dont se comporte un programme sous
Linux, en C++, quand on essaye d'allouer plus de mémoire que
disponible sur le système. À la base, l'opérateur new lance dans ce
cas-là une exception et c'est à moi de la rattraper et de la traiter,
très bien.

Mais mon problème est de savoir si, par derrière, le noyau essaye
d'allouer toute la mémoire avant de se rendre compte qu'il n'en a pas
assez de disponible, ou est-ce qu'il fait d'abord une vérification
avant de se lancer là-dedans ?


C'est le fameux problème de l'"overcommit". Probablement tous les
systèmes Unix modernes, et certainement Linux et FreeBSD font comme les
banquiers qui prêtent de l'argent qu'ils n'ont pas, ils allouent de la
mémoire qui n'est pas disponible, et on voit *aprés* si dès fois il y a
un problème. Il paraît que faire comme ça est indispensable pour
préserver les performances. La conclusion est que si on veut s'assurer
d'avoir rééllement la mémoire disponible, il faut aller écrire dans
chaque page en trappant le signal SEGV. Autrement dit, ça doit
probablement dire que ce qu'on attend de l'opérateur new en C++ ne
fonctionne pas du tout. Il n'y a aucun garantie que la mémoire sera
rééllement disponible sans effectuer des opérations de plus bas niveau
que celles que le C++ fournit en standard.

Tu peux lire l'avis d'experts ici:
http://leaf.dragonflybsd.org/mailarchive/kernel/2005-02/msg00239.html


--

Michel TALON

Avatar
Rémi Moyen
On Feb 16, 10:46 am, (Michel Talon) wrote:
Rémi Moyen wrote:
Salut,

J'ai une question sur la manière dont se comporte un programme sous
Linux, en C++, quand on essaye d'allouer plus de mémoire que
disponible sur le système. À la base, l'opérateur new lance dans ce
cas-là une exception et c'est à moi de la rattraper et de la traite r,
très bien.

Mais mon problème est de savoir si, par derrière, le noyau essaye
d'allouer toute la mémoire avant de se rendre compte qu'il n'en a pas
assez de disponible, ou est-ce qu'il fait d'abord une vérification
avant de se lancer là-dedans ?


C'est le fameux problème de l'"overcommit". Probablement tous les
systèmes Unix modernes, et certainement Linux et FreeBSD font comme les
banquiers qui prêtent de l'argent qu'ils n'ont pas, ils allouent de la
mémoire qui n'est pas disponible, et on voit *aprés* si dès fois il y a
un problème. Il paraît que faire comme ça est indispensable pour
préserver les performances. La conclusion est que si on veut s'assurer
d'avoir rééllement la mémoire disponible, il faut aller écrire da ns
chaque page en trappant le signal SEGV. Autrement dit, ça doit
probablement dire que ce qu'on attend de l'opérateur new en C++ ne
fonctionne pas du tout. Il n'y a aucun garantie que la mémoire sera
rééllement disponible sans effectuer des opérations de plus bas niv eau
que celles que le C++ fournit en standard.


Hmmm... Je crois que je me suis mal exprimé, ou alors que je ne
comprends pas exactement ta réponse. Si je te comprends bien, tu dis
que dans certains cas, 'new' pourrait réussir alors qu'en réalité, le
jour où j'essaierais d'écrire dans ma mémoire, ça ne marchera pas
parce qu'en réalité le noyau m'a alloué des pages qui ne sont pas
vraiment disponibles ? Si c'est le cas, c'est en effet plutôt (!)
génant. Ceci dit, dans mon cas (j'ai testé), mes 'new' me renvoient
bien une exception quand y'a plus de mémoire, donc ce problème n'a pas
l'air d'arriver. Enfin, pas jusqu'à présent...

Mais ma question était plutôt de savoir si le noyau allait passer du
temps à essayer d'allouer avant de me dire qu'il ne peut pas ou si il
était capable de me dire 'non' avant d'avoir tout essayé ? Ta réponse
me laisse supposer que c'est plutôt la première alternative que la
deuxième, c'est bien ça ?

Tu peux lire l'avis d'experts ici:http://leaf.dragonflybsd.org/mailarchiv e/kernel/2005-02/msg00239.html


J'ai lu rapidement et j'avoue ne pas avoir tout compris, mais il me
semble qu'une des conclusions, ça serait que c'est à mon programme de
ne pas demander plus de mémoire que ce qui est raisonnable (et qu'un
des problèmes est de définir ce "raisonnable" proprement), et que je
ne devrais jamais me reposer sur le noyau pour me dire si oui ou non
j'ai assez de mémoire. Ai-je bien compris ?

Dans ce cas, j'ai l'impression que ça veut dire qu'il faut plus ou
moins que j'implémente un micro-garbage collector pour savoir combien
de mémoire j'utilise ?? Ça me semble bizarre, j'ai dû mal
comprendre...

Merci pour ta réponse, en tout cas !
--
Rémi Moyen


Avatar
Pascal Bourguignon
"Rémi Moyen" writes:

Hmmm... Je crois que je me suis mal exprimé, ou alors que je ne
comprends pas exactement ta réponse. Si je te comprends bien, tu dis
que dans certains cas, 'new' pourrait réussir alors qu'en réalité, le
jour où j'essaierais d'écrire dans ma mémoire, ça ne marchera pas
parce qu'en réalité le noyau m'a alloué des pages qui ne sont pas
vraiment disponibles ? Si c'est le cas, c'est en effet plutôt (!)
génant. Ceci dit, dans mon cas (j'ai testé), mes 'new' me renvoient
bien une exception quand y'a plus de mémoire, donc ce problème n'a pas
l'air d'arriver. Enfin, pas jusqu'à présent...


La différence, c'est que le gestionnaire de mémoire doit écrire dans
chaque page allouée lui-même. Mais si tu appelle directement mmap ou
brk ou sbrk, sans écrire dans les pages allouées, effectivement tu
peux obtenir l'exception plus tard, quand tu accéderas à ces pages.


Mais ma question était plutôt de savoir si le noyau allait passer du
temps à essayer d'allouer avant de me dire qu'il ne peut pas ou si il
était capable de me dire 'non' avant d'avoir tout essayé ?


Quand il n'y a plus de mémoire disponible, le noyau commence à tuer
des processus un peu au hasard pour faire de la place. Alors il doit
forcémener passer un peu de temps pour essayer d'allouer... Mais ça,
c'est seulement quand on a rempli toute la mémoire RAM et swap
disponible, et seulement quand on a effectivement besoin de la place,
pas quand on "l'alloue". Sinon le noyau alloue la mémoire sans la
réserver. La seule chose qu'il fait, c'est qu'il mappe toute la
mémoire allouée sur une page mise à zéro en lecture seule. C'est
quand on essaye d'écrire dans cette page que le système va essayer
d'allouer effectivement une page de mémoire et s'il n'en reste plus de
libre en RAM, il va swapper, et s'il n'en reste plus de libre en swap
il va envoyer des signaux (ce qui peut tuer des processus).


Tu peux lire l'avis d'experts ici:http://leaf.dragonflybsd.org/mailarchive/kernel/2005-02/msg00239.html


J'ai lu rapidement et j'avoue ne pas avoir tout compris, mais il me
semble qu'une des conclusions, ça serait que c'est à mon programme de
ne pas demander plus de mémoire que ce qui est raisonnable (et qu'un
des problèmes est de définir ce "raisonnable" proprement), et que je
ne devrais jamais me reposer sur le noyau pour me dire si oui ou non
j'ai assez de mémoire. Ai-je bien compris ?


Oui.


Dans ce cas, j'ai l'impression que ça veut dire qu'il faut plus ou
moins que j'implémente un micro-garbage collector pour savoir combien
de mémoire j'utilise ?? Ça me semble bizarre, j'ai dû mal
comprendre...


Oui. Tu peux avoir une enveloppe fixe, ou évaluer la mémoire
nécessaire en fonction de la taille des données. Tu as de toutes
façon la limite due à l'espace adressable (moins la place pour le
programme, les piles, les bibliothèques, etc). Finalement, c'est une
question d'exploitation: c'est aux opérateurs d'ordonancer les tâches
pour éviter de lancer en même temps deux programmes qui nécessitent
plus que la mémoire physique disponible dans l'ordinateur (et même,
rester de préférence en dessous de la RAM, car si on commence à
swapper, on va rallentire énormément les traitements).


Si on spécifiait pour chaque programme les limites d'espace mémoire
(et temps CPU) nécessaires, on pourrait aussi écrire un ordonanceur
automatique, ce qui se fait dans les grosses exploitations.


--
__Pascal Bourguignon__ http://www.informatimago.com/

"By filing this bug report you have challenged the honor of my
family. Prepare to die!"


Avatar
Rémi Moyen
On Feb 16, 11:55 am, Pascal Bourguignon
wrote:
"Rémi Moyen" writes:
Hmmm... Je crois que je me suis mal exprimé, ou alors que je ne
comprends pas exactement ta réponse. Si je te comprends bien, tu dis
que dans certains cas, 'new' pourrait réussir alors qu'en réalité , le
jour où j'essaierais d'écrire dans ma mémoire, ça ne marchera p as
parce qu'en réalité le noyau m'a alloué des pages qui ne sont pas
vraiment disponibles ? Si c'est le cas, c'est en effet plutôt (!)
génant. Ceci dit, dans mon cas (j'ai testé), mes 'new' me renvoient
bien une exception quand y'a plus de mémoire, donc ce problème n'a pas
l'air d'arriver. Enfin, pas jusqu'à présent...


La différence, c'est que le gestionnaire de mémoire doit écrire dans
chaque page allouée lui-même. Mais si tu appelle directement mmap ou
brk ou sbrk, sans écrire dans les pages allouées, effectivement tu
peux obtenir l'exception plus tard, quand tu accéderas à ces pages.


OK, je comprends ça. Dans mon cas où je passe par un 'new' (qui doit
être défini quelque part dans la stdlibc++, je suppose), est-ce que je
peux supposer que 'new' s'occuppe d'écrire dans chaque page pour
vérifier qu'elle est là, ou pas ? Est-ce que quelqu'un sait où est-ce
que je pourrais savoir ça (à part lire le code source de la libstdc++,
mais bon...) ?

Mais ma question était plutôt de savoir si le noyau allait passer du
temps à essayer d'allouer avant de me dire qu'il ne peut pas ou si il
était capable de me dire 'non' avant d'avoir tout essayé ?


Quand il n'y a plus de mémoire disponible, le noyau commence à tuer
des processus un peu au hasard pour faire de la place. Alors il doit
forcémener passer un peu de temps pour essayer d'allouer... Mais ça,
c'est seulement quand on a rempli toute la mémoire RAM et swap
disponible, et seulement quand on a effectivement besoin de la place,
pas quand on "l'alloue". Sinon le noyau alloue la mémoire sans la
réserver. La seule chose qu'il fait, c'est qu'il mappe toute la
mémoire allouée sur une page mise à zéro en lecture seule. C'est
quand on essaye d'écrire dans cette page que le système va essayer
d'allouer effectivement une page de mémoire et s'il n'en reste plus de
libre en RAM, il va swapper, et s'il n'en reste plus de libre en swap
il va envoyer des signaux (ce qui peut tuer des processus).


D'accord. Donc, si je demande plus de mémoire que disponible, suivant
le comportement de 'new', il va se passer:
- soit 'new' n'écrit pas dans les pages réservées, l'appel est donc
très rapide, mais peut retourner sans échec et ça se passera mal quand
j'essaierais ensuite d'accéder pour de vrai à la mémoire (comment se
manifeste ce 'mal' ? Le noyau tuera des processus au hasard pour cause
de OOM ? Ou quelque chose de plus subtil et de récupérable ?) ;
- soit 'new' écrit dans chaque page pour vérifier sa disponibilité,
l'appel est donc potentiellement lent (parce qu'il faut aller dans
chaque page, potentiellement swapper, etc.), mais si il retourne sans
échec je suis certain que ma mémoire est dispo (bien que peut-être
partiellement sur le swap et donc lente à accéder, mais c'est un peu
un autre problème).

C'est correct ?

Dans ce cas, j'ai l'impression que ça veut dire qu'il faut plus ou
moins que j'implémente un micro-garbage collector pour savoir combien
de mémoire j'utilise ?? Ça me semble bizarre, j'ai dû mal
comprendre...


Oui. Tu peux avoir une enveloppe fixe, ou évaluer la mémoire
nécessaire en fonction de la taille des données.


Pffou... ça ne m'arrange pas du tout, ça. Y'a des fonctions classiques
pour faire ça facilement (en C++, toujours) ? Genre un appel système
qui fixerait la taille maxi que je veux et qui ferait en sorte que
n'importe quel 'new' allouant au-dessus de cette limite lance une
exception ? Ça devrait être le comportement par défaut, me semble-t-
il...

Merci pour tes précisions, je cerne mieux le problème.
--
Rémi Moyen


Avatar
Rémi Moyen
On Feb 16, 11:31 am, Loic Tortay wrote:
["overcommit" de mémoire]

Hmmm... Je crois que je me suis mal exprimé, ou alors que je ne
comprends pas exactement ta réponse. Si je te comprends bien, tu dis
que dans certains cas, 'new' pourrait réussir alors qu'en réalité , le
jour où j'essaierais d'écrire dans ma mémoire, ça ne marchera p as
parce qu'en réalité le noyau m'a alloué des pages qui ne sont pas
vraiment disponibles ? Si c'est le cas, c'est en effet plutôt (!)
génant. Ceci dit, dans mon cas (j'ai testé), mes 'new' me renvoient
bien une exception quand y'a plus de mémoire, donc ce problème n'a pas
l'air d'arriver. Enfin, pas jusqu'à présent...


Sous Linux (entre autres), c'est un paramètre de fonctionnement
configurable avec "sysctl" (ou équivalent).

Sous Linux, on peut donc utiliser la commande
"sysctl vm.overcommit_memory" pour voir si c'est activé ou pas.


D'après le fil de discussion cité par Michel Talon, il semblerait que
désactiver l'overcommit ne soit pas très judicieux (en plus de n'être
faisable que par root, je crois), et j'ai de toute façon l'impression
que ça ne réglera pas vraiment mon problème (à savoir éviter que 'new'
mette une heure avant de me dire que finalement il n'a pas assez de
mémoire disponible pour faire ce que je veux).
--
Rémi Moyen


Avatar
Rémi Moyen
On Feb 16, 1:01 pm, Loic Tortay wrote:
[...]

Sous Linux (entre autres), c'est un paramètre de fonctionnement
configurable avec "sysctl" (ou équivalent).

Sous Linux, on peut donc utiliser la commande
"sysctl vm.overcommit_memory" pour voir si c'est activé ou pas.


D'après le fil de discussion cité par Michel Talon, il semblerait q ue
désactiver l'overcommit ne soit pas très judicieux (en plus de n' être
faisable que par root, je crois), et j'ai de toute façon l'impression
que ça ne réglera pas vraiment mon problème (à savoir éviter que 'new'
mette une heure avant de me dire que finalement il n'a pas assez de
mémoire disponible pour faire ce que je veux).


Que ce soit judicieux ou pas n'a rien à voir avec le fait que ce soit
désactivé.


Ben si c'était à moi de décider, je préférerais prendre une déc ision
judicieuse, quand même. Mais bon, je ne prétends pas comprendre tous
les arguments à ce sujet et je laisse les autres décider là-dessus.
Par exemple les gestionnaires de Redhat que tu évoques savent
infiniment mieux que moi ce qui est judicieux ou pas à ce sujet.

Dans tous les cas, "new" ne prend une heure pour allouer de la mémoire
que si la machine est déjà surchargée (ou la mémoire libre très
morcellée).


Ah ben ça, c'est une réponse qui me plait plus, et qui me conviendrait
parfaitement. Mais ça n'a pas vraiment l'air d'être en accord avec ce
que dit Pascal Bourguignon, je suis donc un peu perdu, là...

Le système de gestion de la mémoire sait combien de place est disponi ble,
il n'avance pas à tatons.


Ben c'est aussi ce que je me disais au début, mais par exemple, je ne
connais pas de fonction pour accéder facilement à la quantité de
mémoire utilisable. Je peux connaître la mémoire libre, mais ce
chiffre ne tient pas compte de la mémoire utilisé pour servir de cache
et qui est disponible si nécessaire. Donc je me dis que le système n'a
pas toujours l'air de pouvoir dire clairement ce qui est réellement
dispo ou pas -- et les commentaires de Michel Talon sur l'overcommit
semblent confirmer cela. D'où ma perplexité.

Il est aussi possible de redéfinir l'opérateur "new" si on pense pouv oir
gérer la mémoire mieux que le système (ou la "libc").


Oui ben non, ça m'étonnerait beaucoup que je sois capable de gérer la
mémoire mieux.
--
Rémi Moyen



Avatar
Marc
Michel Talon wrote:

C'est le fameux problème de l'"overcommit". Probablement tous les
systèmes Unix modernes, et certainement Linux et FreeBSD font comme les
banquiers qui prêtent de l'argent qu'ils n'ont pas, ils allouent de la
mémoire qui n'est pas disponible, et on voit *aprés* si dès fois il y a
un problème. Il paraît que faire comme ça est indispensable pour
préserver les performances.


Tu oublies solaris qui ne fait pas d'overcommit (il n'y a aucune option
pour le permettre) et s'en sort bien quand même.

Avatar
JKB
Le 16-02-2007, à propos de
Re: Allouer plus de mémoire que disponible,
Marc écrivait dans fr.comp.os.unix :
Michel Talon wrote:

C'est le fameux problème de l'"overcommit". Probablement tous les
systèmes Unix modernes, et certainement Linux et FreeBSD font comme les
banquiers qui prêtent de l'argent qu'ils n'ont pas, ils allouent de la
mémoire qui n'est pas disponible, et on voit *aprés* si dès fois il y a
un problème. Il paraît que faire comme ça est indispensable pour
préserver les performances.


Tu oublies solaris qui ne fait pas d'overcommit (il n'y a aucune option
pour le permettre) et s'en sort bien quand même.


Certes, mais il ne faut pas perdre de vue que la gestion de la
mémoire sous Linux (par exemple) et sous Solaris n'a rien à voir.
Sur une antique U1E avec 640 Mo de mémoire; j'arrive sous Solaris 9,
à faire tourner une grosse application java (serveur) avec un
netbeans sans que le système ne commence à swapper ne serait-ce
qu'un octet. Sous Linux, le simple fait d'ouvrir la même application
me fait swapper comme un dingue...

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.


Avatar
Nicolas George
Michel Talon wrote in message <er422f$1o3h$:
C'est le fameux problème de l'"overcommit".


Pas seulement.

Il y a deux niveaux :

- La noyau sait combien de mémoire virtuelle il lui reste, et si un
processus demande trop de mémoire, il lui répond non.

- Le noyau ne choisit pas immédiatement la mémoire qu'il attribue au
processus, il se contente de noter la quantité dans ses tables. Les pages
ne sont effectivement allouées que quand le processus y accède.

L'overcommit joue entre ces deux effets : si un processus demande beaucoup
mais utilise peu, alors les pages en rab peuvent être disponibles pour
d'autres processus.

banquiers qui prêtent de l'argent qu'ils n'ont pas


Légalement, les banquiers créent l'argent qu'ils prêtent.

1 2 3 4 5