56 votes

L'allocation de mémoire dynamique diffère-t-elle en C et C ++ dans les implémentations courantes?

Aussi loin que la langue respective normes, C offre allocation dynamique de la mémoire uniquement par le biais de l' malloc() de la famille, alors qu'en C++ la forme la plus commune de l'allocation est effectué par ::operator new(). Le style C malloc est également disponible en C++, et de nombreux "bébé allocateur" exemples de l'utiliser comme base de répartition de fonction, mais je suis curieux de voir comment contemporains compilateurs de mettre en œuvre la production réelle de l'opérateur new.

Est-ce juste un wrapper mince autour d' malloc(), ou est-il mis en œuvre fondamentalement différent sur le compte de la assez différente de l'allocation de mémoire le comportement typique d'un programme en C++ par rapport à un type de programme C?

[Edit: je crois que la principale différence est généralement décrit comme suit: Un programme C a de moins en moins, de plus, à long terme affectations, tout un programme C++ a de nombreuses, petites, de courte durée des allocations. N'hésitez pas à carillon si c'est tromper, mais il semble que l'on aurait avantage à prendre en compte.]

Un compilateur comme GCC, il serait facile de n'avoir qu'une seule allocation de base de la mise en œuvre et l'utiliser pour toutes les langues, donc je me demande si il y a des différences dans les détails que d'essayer d'optimiser l'attribution de performance dans chaque langue.


Mise à jour: Merci à tous pour les réponses grands! Il ressemble dans GCC c'est complètement résolu par ptmalloc, et que MSVC utilise également malloc à la base. Personne ne sait comment le MSVC malloc est mis en œuvre?

48voto

NPE Points 169956

Ici est la mise en œuvre utilisée par g++ 4.6.1:

_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz) throw (std::bad_alloc)
{
  void *p;

  /* malloc (0) is unpredictable; avoid it.  */
  if (sz == 0)
    sz = 1;
  p = (void *) malloc (sz);
  while (p == 0)
    {
      new_handler handler = __new_handler;
      if (! handler)
#ifdef __EXCEPTIONS
        throw bad_alloc();
#else
        std::abort();
#endif
      handler ();
      p = (void *) malloc (sz);
    }

  return p;
}

Cela se trouve dans libstdc++-v3/libsupc++/new_op.cc à l'intérieur de la g++ la distribution des sources.

Comme vous pouvez le voir, c'est un assez mince wrapper autour de malloc.

modifier Sur de nombreux systèmes, il est possible d'affiner le comportement de l' malloc, généralement en appelant mallopt ou de définition des variables d'environnement. Voici un article de discuter de certaines fonctions disponibles sur Linux.

Selon Wikipedia, glibc versions 2.3+ utiliser une version modifiée de l'allocateur appelés ptmalloc, qui lui-même est un dérivé de la dlmalloc dessiné par Doug Lea. Il est intéressant de noter, dans un article sur dlmalloc Doug Lea donne le point de vue (l'emphase est mienne):

J'ai écrit la première version de l'allocateur après avoir écrit en C++ les programmes qui reposait presque exclusivement sur l'allocation dynamique de la mémoire. J'ai trouvé qu'ils couraient beaucoup plus lentement et/ou avec beaucoup plus de total la consommation de mémoire que ce que j'attendais d'eux. Cela était dû à caractéristiques de l'allocateurs de mémoire sur les systèmes que je courais (principalement les versions actuelles de SunOs et BSD ). Pour contrer ce, j'ai d'abord écrit un certain nombre de but spécial les allocateurs en C++, normalement par la surcharge de l'opérateur new pour les différentes classes. Certains de ces dernières sont décrites dans un livre sur le C++ techniques de répartition qui a été adapté en 1989, C++ Rapport à l'article, Certains d'allocation de stockage techniques pour les classes conteneur.

Cependant, j'ai vite réalisé que la construction d'un spécial de l'allocateur pour chaque nouvelle classe qui ont tendance à être attribuées de manière dynamique et très utilisée a été pas une bonne stratégie lors de la construction de types de programmation à usage général des cours de soutien que j'écrivais à l'époque. (De 1986 à 1991, j'ai été le principal auteur de libg++ , le C++ de GNU library.) Une plus large la solution était nécessaire -- pour écrire un allocateur qui était assez bon dans des conditions normales de C++ et de C charge de sorte que les programmeurs ne serait pas tenté pour écrire à des fins spéciales de allocateurs, sauf dans des conditions.

Cet article présente une description de certains des principaux objectifs de la conception, algorithmes, et les considérations de mise en œuvre de cette allocation.

15voto

sharptooth Points 93379

Dans la plupart des mises en œuvre, operator new() appelle uniquement malloc() . En fait, même The Standard suggère cela comme stratégie par défaut . Bien sûr, vous pouvez implémenter vos propres operator new , généralement pour une classe si vous souhaitez obtenir de meilleures performances, mais la valeur par défaut consiste généralement à appeler malloc() .

12voto

cyco130 Points 2577

glibc new operator est une mince couche autour de malloc. Et glibc malloc utilise différentes stratégies pour différentes requêtes d'allocation de taille. Vous pouvez voir la mise en œuvre, ou au moins les commentaires ici .

Voici un extrait des commentaires dans malloc.c:

 /*
47   This is not the fastest, most space-conserving, most portable, or
48   most tunable malloc ever written. However it is among the fastest
49   while also being among the most space-conserving, portable and tunable.
50   Consistent balance across these factors results in a good general-purpose
51   allocator for malloc-intensive programs.
52 
53   The main properties of the algorithms are:
54   * For large (>= 512 bytes) requests, it is a pure best-fit allocator,
55     with ties normally decided via FIFO (i.e. least recently used).
56   * For small (<= 64 bytes by default) requests, it is a caching
57     allocator, that maintains pools of quickly recycled chunks.
58   * In between, and for combinations of large and small requests, it does
59     the best it can trying to meet both goals at once.
60   * For very large requests (>= 128KB by default), it relies on system
61     memory mapping facilities, if supported.
*/
 

10voto

In silico Points 30778

Sous Visual C ++, entrer dans une expression new me conduit à cet extrait de code new.cpp :

 #include <cstdlib>
#include <new>

_C_LIB_DECL
int __cdecl _callnewh(size_t size) _THROW1(_STD bad_alloc);
_END_C_LIB_DECL

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
        {       // try to allocate size bytes
        void *p;
        while ((p = malloc(size)) == 0)
                if (_callnewh(size) == 0)
                {       // report no memory
                static const std::bad_alloc nomem;
                _RAISE(nomem);
                }

        return (p);
        }
 

Ainsi, les new VC ++ encapsulent également l 'appel malloc() .

2voto

Emilio Garavaglia Points 9189

Ce n’est pas une question de performance: pA = new A a un effet secondaire différent de pA = (A*)malloc(sizeof(A));

Dans le second cas, le constructeur de A n'est pas appelé. Pour arriver au même effet, vous devriez faire

 pA = (A*)malloc(sizeof(A));
new(pA)A();
 

où new est le "placement new" ...

 void* operator new(size_t sz, void* place) 
{ return place; }
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X