web-dev-qa-db-fra.com

mémoire maximale que malloc peut allouer

J'essayais de déterminer la quantité de mémoire que je peux utiliser au maximum sur ma machine (1 Go RAM 160 Go HD Windows Platform).

J'ai lu que la mémoire maximale que malloc peut allouer est limitée à la mémoire physique (sur le tas).

De plus, lorsqu'un programme dépasse la consommation de mémoire à un certain niveau, l'ordinateur cesse de fonctionner car les autres applications n'obtiennent pas suffisamment de mémoire dont elles ont besoin.

Donc pour confirmer, j'ai écrit un petit programme en C:

int main(){  
    int *p;
    while(1){
        p=(int *)malloc(4);
        if(!p)break;
    }   
}

J'espérais qu'il y aurait un moment où l'allocation de mémoire échouerait et la boucle se briserait, mais mon ordinateur s'est bloqué car c'était une boucle infinie.

J'ai attendu environ une heure et j'ai finalement dû forcer l'arrêt de mon ordinateur.

Quelques questions:

  • Malloc alloue-t-il également de la mémoire à partir de la HD?
  • Quelle était la raison du comportement ci-dessus?
  • Pourquoi la boucle n'a-t-elle pas été interrompue à un moment donné?
  • Pourquoi n'y a-t-il pas eu d'échec d'allocation?
41
Vikas

J'ai lu que la mémoire maximale malloc peut allouer est limitée à la mémoire physique (sur le tas).

Mauvais: la plupart des ordinateurs/systèmes d'exploitation prennent en charge la mémoire virtuelle , soutenue par l'espace disque.

Quelques questions: malloc alloue-t-il également la mémoire du disque dur?

malloc demande au système d'exploitation, qui à son tour pourrait bien utiliser de l'espace disque.

Quelle était la raison du comportement ci-dessus? Pourquoi la boucle ne s'est-elle interrompue à aucun moment?

Pourquoi n'y a-t-il pas eu d'échec d'allocation?

Vous venez d'en demander trop peu à la fois: la boucle se serait finalement rompue (bien après que votre machine ait ralenti pour une analyse en raison du grand excès de mémoire virtuelle et physique et de l'accès au disque super fréquent qui en résulte, un problème connu sous le nom de "thrashing" ") mais cela a épuisé votre patience bien avant. Essayez d'obtenir par exemple un mégaoctet à la fois.

Lorsqu'un programme dépasse la consommation de mémoire à un certain niveau, l'ordinateur cesse de fonctionner car les autres applications n'obtiennent pas suffisamment de mémoire dont elles ont besoin.

Un arrêt total est peu probable, mais lorsqu'une opération qui prendrait normalement quelques microsecondes finit par prendre (par exemple) des dizaines de millisecondes, ces quatre ordres de grandeur peuvent certainement le faire sentir comme si l'ordinateur s'était pratiquement arrêté, et ce qui prendrait normalement une minute pourrait prendre une semaine.

47
Alex Martelli

Je sais que ce fil est ancien, mais pour tous ceux qui veulent l'essayer, utilisez ce code coupé

#include <stdlib.h>

int main() {
int *p;
while(1) {
    int inc=1024*1024*sizeof(char);
    p=(int*) calloc(1,inc);
    if(!p) break;
    }
}

courir

$ gcc memtest.c
$ ./a.out

lors de l'exécution, ce code remplit les uns RAM jusqu'à ce qu'il soit tué par le noyau. Utiliser calloc au lieu de malloc pour empêcher "l'évaluation paresseuse". Idées tirées de ce fil: Questions sur la mémoire Malloc

Ce code a rapidement rempli mon RAM (4Gb) puis en environ 2 minutes ma partition de swap de 20 Go avant sa mort. Linux 64 bits bien sûr.

25
Sebastian

malloc fait sa propre gestion de la mémoire, gérant lui-même les petits blocs de mémoire, mais en fin de compte, il utilise les fonctions Win32 Heap pour allouer de la mémoire. Vous pouvez considérer malloc comme un "revendeur de mémoire".

Le sous-système de mémoire Windows comprend la mémoire physique (RAM) et la mémoire virtuelle (HD). Lorsque la mémoire physique devient rare, certaines pages peuvent être copiées de la mémoire physique vers la mémoire virtuelle sur le disque dur. Windows le fait de manière transparente.

Par défaut, la mémoire virtuelle est activée et consommera l'espace disponible sur le disque dur. Ainsi, votre test continuera de s'exécuter jusqu'à ce qu'il ait alloué la quantité totale de mémoire virtuelle pour le processus (2 Go sur des fenêtres 32 bits) ou rempli le disque dur.

6
mdma

Essaye ça

#include <stdlib.h>
#include <stdio.h>

main() {
    int Mb = 0;
    while (malloc(1<<20)) ++Mb;
    printf("Allocated %d Mb total\n", Mb);
}

Incluez stdlib et stdio pour cela.
Cet extrait est tiré de secrets c profonds.

5
human.js

Je ne sais pas vraiment pourquoi cela a échoué, mais une chose à noter est que `malloc (4)" ne peut pas réellement vous donner 4 octets, donc cette technique n'est pas vraiment un moyen précis de trouver votre taille de tas maximale.

J'ai découvert cela à partir de ma question ici .

Par exemple, lorsque vous déclarez 4 octets de mémoire, l'espace juste avant votre mémoire peut contenir l'entier 4, comme indication au noyau de la quantité de mémoire que vous avez demandée.

3
Chris Cooper

Conformément à la norme C90, vous pouvez obtenir au moins un objet d'une taille de 32 Ko, ce qui peut être de la mémoire statique, dynamique ou automatique. C99 garantit au moins 64 Ko. Pour toute limite supérieure, reportez-vous à la documentation de votre compilateur.

De plus, l'argument de malloc est un size_t et la plage de ce type est [0, SIZE_MAX], donc le maximum que vous pouvez demande est SIZE_MAX, dont la valeur varie selon l'implémentation et est définie dans <limits.h>.

2
mav_2k

/proc/sys/vm/overcommit_memory contrôle le maximum sur Linux

Sur Ubuntu 19.04 par exemple, on peut facilement voir que malloc est implémenté avec mmap(MAP_ANONYMOUS en utilisant strace .

Ensuite man proc décrit ensuite comment /proc/sys/vm/overcommit_memory contrôle l'allocation maximale:

Ce fichier contient le mode de comptabilité de la mémoire virtuelle du noyau. Les valeurs sont:

  • 0: surcharge heuristique (c'est la valeur par défaut)
  • 1: toujours surcharger, ne jamais vérifier
  • 2: vérifiez toujours, ne surchargez jamais

En mode 0, les appels de mmap (2) avec MAP_NORESERVE ne sont pas vérifiés, et la vérification par défaut est très faible, entraînant le risque d'obtenir un processus "OOM-kill".

En mode 1, le noyau prétend qu'il y a toujours suffisamment de mémoire, jusqu'à ce que la mémoire soit réellement épuisée. Un cas d'utilisation pour ce mode est les applications de calcul scientifique qui utilisent de grands tableaux clairsemés. Dans les versions du noyau Linux antérieures à 2.6.0, toute valeur différente de zéro implique le mode 1.

En mode 2 (disponible depuis Linux 2.6), l'espace d'adressage virtuel total qui peut être alloué (CommitLimit dans/proc/meminfo) est calculé comme suit:

CommitLimit = (total_RAM - total_huge_TLB) * overcommit_ratio / 100 + total_swap

où:

  • total_RAM est le montant total de RAM sur le système;
  • total_huge_TLB est la quantité de mémoire réservée aux pages volumineuses;
  • overcommit_ratio est la valeur dans/proc/sys/vm/overcommit_ratio; et
  • total_swap est la quantité d'espace de swap.

Par exemple, sur un système avec 16 Go de RAM physique, 16 Go de swap, aucun espace dédié aux pages volumineuses et un taux de surcharge de 50, cette formule donne une limite de 24 Go.

Depuis Linux 3.14, si la valeur dans/proc/sys/vm/overcommit_kbytes n'est pas nulle, alors CommitLimit est plutôt calculé comme suit:

CommitLimit = overcommit_kbytes + total_swap

Voir aussi la description de/proc/sys/vm/admiin_reserve_kbytes et/proc/sys/vm/user_reserve_kbytes.

Documentation/vm/overcommit-accounting.rst dans l'arborescence du noyau 5.2.1 donne également quelques informations, bien que lol un peu moins:

Le noyau Linux prend en charge les modes de gestion de sur-engagement suivants

  • 0 Gestion heuristique des surcharges. Les surcharges évidentes d'espace d'adressage sont refusées. Utilisé pour un système typique. Il garantit qu'une allocation sérieusement sauvage échoue tout en permettant un surengagement pour réduire l'utilisation du swap. root est autorisé à allouer un peu plus de mémoire dans ce mode. C'est la valeur par défaut.

  • 1 Toujours trop engager. Convient à certaines applications scientifiques. Un exemple classique est le code utilisant des tableaux clairsemés et s'appuyant uniquement sur la mémoire virtuelle constituée presque entièrement de zéro page.

  • 2 Ne vous engagez pas trop. La validation de l'espace d'adressage total pour le système n'est pas autorisée à dépasser le swap + une quantité configurable (la valeur par défaut est 50%) de RAM physique. Selon la quantité que vous utilisez, dans la plupart des situations, cela signifie qu'un processus ne sera pas interrompu lors de l'accès aux pages mais recevra des erreurs d'allocation de mémoire, le cas échéant.

    Utile pour les applications qui souhaitent garantir leurs allocations de mémoire seront disponibles à l'avenir sans avoir à initialiser chaque page.

Expérience minimale

Nous pouvons facilement voir la valeur maximale autorisée avec:

principal c

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char **argv) {
    char *chars;
    size_t nbytes;

    /* Decide how many ints to allocate. */
    if (argc < 2) {
        nbytes = 2;
    } else {
        nbytes = strtoull(argv[1], NULL, 0);
    }

    /* Allocate the bytes. */
    chars = mmap(
        NULL,
        nbytes,
        PROT_READ | PROT_WRITE,
        MAP_SHARED | MAP_ANONYMOUS,
        -1,
        0
    );

    /* This can happen for example if we ask for too much memory. */
    if (chars == MAP_FAILED) {
        perror("mmap");
        exit(EXIT_FAILURE);
    }

    /* Free the allocated memory. */
    munmap(chars, nbytes);

    return EXIT_SUCCESS;
}

GitHub en amont .

Compilez et exécutez pour allouer 1GiB et 1TiB:

gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out 0x40000000
./main.out 0x10000000000

Nous pouvons ensuite jouer avec la valeur d'allocation pour voir ce que le système permet.

Je ne trouve pas de documentation précise pour 0 (par défaut), mais sur mon 32 Go RAM machine, il n'autorise pas l'allocation de 1 To:

mmap: Cannot allocate memory

Cependant, si j'active la surcharge illimitée:

echo 1 | Sudo tee /proc/sys/vm/overcommit_memory

alors l'allocation de 1 To fonctionne bien.

Mode 2 est bien documenté, mais je suis paresseux pour effectuer des calculs précis pour le vérifier. Mais je voudrais simplement souligner qu'en pratique, nous sommes autorisés à allouer environ:

overcommit_ratio / 100

de RAM totale et overcommit_ratio est 50 par défaut, nous pouvons donc allouer environ la moitié de la RAM totale.

VSZ vs RSS et le tueur de mémoire insuffisante

Jusqu'à présent, nous venons d'allouer de la mémoire virtuelle.

Cependant, à un moment donné, si vous utilisez suffisamment de ces pages, Linux devra commencer à tuer certains processus.

J'ai illustré cela en détail sur: Qu'est-ce que RSS et VSZ dans la gestion de la mémoire Linux