9
votes

Pourquoi ce programme se réserve-t-il et conserve 2 Go de mémoire au lieu de 1 Go?

#include <vector>
typedef std::vector<char> vc;
typedef std::vector<vc> vvc;

vvc f() {
  const int N = (1 << 15);
  return vvc(N, vc(N)); // 1 GB
}

int main () {
  vvc v; 
  v = f();
  while(true);  // Why 2GB allocated ?
  return 0;
}
Compiled with both clang -O2 and g++ -O2. Same behavior.Edit: There are multiple ways to fix this code pointed in some of the answers.
But my point is to understand this code. Of course there is a temporary object, but it should disappear at the semicolon and 1GB of memory should be returned to the system. The question intends to ask why it does not happen.Edit 2: The destructor of temporary object is indeed called before semicolon.

4 commentaires

C'est la chose d'affectation. Avec std = c ++ 11 c'est 1 Go.


Avez-vous essayé de récupérer la mémoire en excès, par exemple en lancant un autre exemple de ceci. Qu'advient-il de la première instance?


4ème processus a écrasé ma machine (8 Go de RAM).


Comment mesurez-vous la mémoire utilisée? Si vous retournez 1 Go à l'allocator, cela ne signifie pas que l'allocator renverra nécessairement 1 Go au système d'exploitation.


5 Réponses :


2
votes

Je viens de tester sur Win32, VC7.

Votre code attribue 2 Go. Si vous changez à ceci: xxx

Il n'a besoin que de 1 Go.

Je suppose que la raison - est une opération de copie entre les vecteurs.

v = f () - invoque le constructeur. Votre cas - Par défaut C'TOR et Copy (opérateur = ) dans un objet vide. Besoins à l'ivresse 2 Go.

Actions internes de F () (Création de vecteur et de retour) peut utiliser rvo , et il n'y a pas d'adaptation et d'allocation supplémentaire.


0 commentaires

0
votes

Je pense que la valeur de retour sera copiée. Que temporaire 2 exemplaires de VVC existe. Chacun avec 2 ^ 30 caractères (= 1 Go)

à ma connaissance, le compilateur est autorisé mais non requis pour supprimer l'opération de copie

J'ai trouvé ce lien http://en.wikipedia.org/wiki/return_value_optimization


1 commentaires

Tu as raison, je pense. Mais voyez mon commentaire à Balog Pal répondre.



4
votes

Vous avez une affectation dans votre code que nous prêchons contre en vain pour une bonne raison; -)

Si vous avez utilisé l'initialisation, cela permet une optimisation de la RVO et que d'autres expérimentées semblent également travailler aussi.

avec affectation La fonction renvoie un objet, puis il doit être copié à la cible et que ce n'est que Nuke le temporaire. Bien que l'analyse de flux de données, elle pourrait être éventuellement optimisée, c'est un cas difficile à attraper et considéré comme rare d'utiliser. Ainsi, vous subissez la pénalité de performance sur tout le reste.

Edit: pour voir que le temporaire est effectivement noué au point approprié, je suggère d'utiliser le débogueur et une étape à une étape. Alternativement regarder la liste des assy. Pour la machine abstrait, l'appel DTor doit se produire avant la prochaine instruction.

Cependant, le code optimisé utilise le «comme si» règne plus libéralement. Si le code n'a aucun moyen de dire la différence, cela peut le reporter un peu et vous pouvez simplement ressentir cet effet.

C ++ 11 Remarque: En C ++ 11 vecteur a gagné une autre op = qui passe de la rvalue. Cela guérirait ce cas particulier, mais il faudra du temps jusqu'à ce que tous les compilateurs fermeront l'écart et surtout toutes les classes gagnent des trucs en mouvement. Le code pré-11 à la recherche de déménagement utiliserait un échange () à la place de =.


12 commentaires

En fait, c'est exactement l'un des résous de résolution de sémantique de déménagement de C ++ 11.


Mais pourquoi F () Frame n'est-elle pas libérée? Il devrait arriver de toute façon, même avant alors que (vrai); . Je ne peux pas voir la demande d'optimisation ici. La mémoire dynamique de base sémantique devrait fonctionner.


@Capellic, il a probablement été publié, le système d'exploitation ne l'a tout simplement pas encore récupéré.


@Capellic, de nombreux allocateurs ne relâchent jamais la mémoire au système d'exploitation, en particulier pour les petits morceaux. Il peut être récupéré dans le processus - la récréation du vecteur ne provoquera pas une augmentation de 3 Go.


@KonRADRUDOLPH: Oui, je pense que cela pourrait être le centre de la question. Sinon, devrait être un gros bug dans ces compilateurs.


@Capellic: Ce qui vous manque, c'est que l'objet renvoyé de la fonction habite dans le cadre de la pile de l'appelant. Le cadre de pile f () est libéré, mais la valeur renvoyée de f () vit dans principale . En tout état de cause, en ignorant que, estimant que les deux vecteurs doivent être vivants en même temps pour que la copie des valeurs réussisse. À ce stade, votre programme consomme 2G de mémoire et l'allocator ne relâchera probablement pas cette mémoire au système d'exploitation. Si vous souhaitez essayer cela, créez votre propre allocator (allocator C ++) et transmettez-le au vecteur, puis de mesurer la mémoire


@ Davidrodríguez-Dribesas: Pourquoi pensez-vous que cela me manque? La valeur en main est une taille de 1 Go (quelque chose de plus), et c'est le point de la question.


Le point de ce code n'est pas comment le réparer mais la comprendre.


@Capellic: "GRATUIT" de la vue du programme et "GRATUITEMENT" de la vue du système d'exploitation n'est pas la même. En effet, dans la plupart des systèmes d'exploitation, le programme ne peut pas donner à 1 Go à l'OS dans cette situation.


@Capellic: Non C'est ce que je pense manquant: la taille maximale requise dans principal est de 2 Go non 1 Go. Dans le contexte de principal la valeur renvoyée de f () et v sont vivants et avec la même taille 1 Go en même temps. Pour une brève période, mais la quantité consommée dans principale à un point est de 2 Go. Semblable à int main () {big1gtype obj1; {big1gtype obj2; }}


Bien que temporairement principal a besoin de 2 Go de 2 Go, en (vrai), il devrait avoir déjà besoin de 1 Go, mais en fait en fait 2 Go.


Avez-vous cherché des choses après 'éditer:'?



4
votes

Je suppose que vous envisagez l'affichage du système d'exploitation de la quantité de mémoire allouée à le processus et que votre compilateur ne prend pas encore en charge l'affectation de déplacement de C ++ 11. < / p>

Par conséquent, ce qui se passe est probablement ce qui suit:

  • Dans votre fonction, vous allouez le vecteur (consommant 1 Go de mémoire).
  • Puis, dans votre mission, ce vecteur est copié (consommer une anmother 1 Go de mémoire, qui est adjacente au premier bloc).
  • Après cela, le vecteur d'origine est détruit. Cela libère la mémoire des allocations futures de votre programme, mais depuis que cette mémoire est avant la mémoire du vecteur retenu, et pour la plupart des systèmes d'exploitation, la mémoire allouée à un processus doit être contiguë, le processus peut < em> pas le renvoyer au système d'exploitation.

    Donc, le résultat est que le système d'exploitation alloue 2 Go à votre processus, dont 1 Go est attribué au vecteur et 1 Go est libre pour une allocation future dans votre programme (mais pas aux allocations d'un autre processus).


0 commentaires

1
votes

EDIT: Il existe plusieurs façons de corriger ce code pointé dans une partie de la réponses. Mais mon point est de comprendre ce code. Bien sûr il y a un objet temporaire, mais il devrait disparaître au point-virgule et 1 Go de La mémoire doit être retournée sur le système. La question a l'intention de demander pourquoi cela ne se produit pas.

sur Windows j'ai compilé votre test avec GCC et Ran. Et je vois que la valeur de d'octets privés augmente après le début du programme, puis commence à diminuer puis cesser de changer. Donc, sur cette mémoire du système d'exploitation est renvoyée au système. À la manière dont j'ai utilisé Process Explorer pour obtenir des informations sur d'octets privés .

Je suppose que vous avez fait votre test sur Linux et sur cette mémoire du système d'exploitation n'est pas retourné au système. S'il n'est pas utilisé, il est finalement déplacé dans une zone de pagination si j'ai raison.


0 commentaires