Отличается ли динамическое выделение памяти в C и C++ в популярных реализациях?



что касается соответствующих языковых стандартов, C предлагает динамическое выделение памяти только через malloc() семья, в то время как в C++ наиболее распространенная форма выделения выполняется ::operator new(). C-style malloc также доступен на C++, и многие примеры "первого распределителя ребенка" используют его в качестве основной функции распределения, но мне любопытно, как современные компиляторы реализуют фактический оператор производства-новый.



это просто тонкая обертка вокруг malloc(), или это реализовано принципиально по-разному из-за довольно различного поведения выделения памяти типичной программы на C++ по сравнению с типичной программой на C?



[Edit: Я считаю, что основное различие обычно описывается следующим образом: программа C имеет меньше, больше, длительные распределения, в то время как программа C++ имеет много, небольшие, кратковременные распределения. Не стесняйтесь перезвонить, если это ошибочно, но похоже, что было бы полезно принять это во внимание счет.]



для компилятора, такого как GCC, было бы легко иметь только одну реализацию выделения ядра и использовать ее для всех соответствующих языков, поэтому мне интересно, есть ли различия в деталях, которые пытаются оптимизировать результирующую производительность выделения на каждом языке.





обновление: Спасибо за хорошие ответы! Похоже, в GCC это полностью решается с помощью ptmalloc, и что MSVC также использует malloc в ядро. Кто-нибудь знает, как реализуется MSVC-malloc?

604   5  

5 ответов:

вот реализация используется 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;
}

это libstdc++-v3/libsupc++/new_op.cc внутри исходного дистрибутива g++.

как вы можете видеть, это довольно тонкая обертка вокруг malloc.

edit на многих системах можно точно настроить поведение malloc, как правило, по телефону mallopt или установка переменных окружения. Вот один статьи обсуждение некоторых функций, доступных на Линукс.

согласно Википедии,glibc версии 2.3 + используйте модифицированную версию распределителя под названием ptmalloc, который сам является производным dlmalloc разработан Дуг Lea. Интересно, в Ан статьи о dlmalloc Дуг Леа дает следующую перспективу (акцент мой):

я написал первую версию распределителя после написания некоторых C++ программы, которые почти исключительно полагался на выделение динамической памяти. Я обнаружил, что они бежали гораздо медленнее и/или с более общей потребление памяти, чем я ожидал от них. Это было связано с характеристики распределителей памяти в системах, в которых я работал on (в основном тогдашние версии SunOs и BSD ). Противостоять это, во-первых, я написал ряд специальных распределителей в C++, обычно путем перегрузки оператора new для различных классов. Некоторые из них они описаны в статье на C++ методы распределения, которые были адаптировано в статье отчета 1989 C++ некоторое распределение памяти методы для контейнерных классов.

тем не менее, я вскоре понял, что создание специального распределителя для каждого новый класс, который, как правило, динамически выделялся и активно использовался, был не очень хорошая стратегия при построении видов программирования общего назначения классы поддержки, которые я писал в то время. (С 1986 по 1991 год, я был основной автор libg++, GNU C++ библиотека.) Более широкий спектр решение было необходимо -- написать распределитель, который был достаточно хорош при обычных нагрузках C++ и C чтобы программисты не соблазнились чтобы написать специальные распределители, за исключением очень специальных условия.

в данной статье представлено описание некоторых основных целей проектирования, алгоритмы и соображения по реализации для этого распределителя.

В большинстве реализаций operator new() просто называет malloc(). На самом деле даже стандартный предполагает, что по умолчанию stratege. Конечно, вы можете реализовать свой собственный operator new, обычно для класса, если вы хотите повысить производительность, но по умолчанию обычно просто вызывает malloc().

новый оператор glibc-это тонкая обертка вокруг malloc. И glibc malloc использует разные стратегии для разных запросов на выделение размера. Вы можете увидеть реализацию, или, по крайней мере, комментарии здесь.

вот выдержка из комментариев в 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.
*/

на Visual C++, шагая в new выражение приводит меня к этому фрагменту в 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);
        }

так VC++ ' s new также обертывания malloc() звонок.

это не вопрос производительности: pA = new A имеет другой побочный эффект, чем pA = (A*)malloc(sizeof(A));

во втором случае конструктор A не вызывается. Чтобы прийти к тому же эффекту, вы должны сделать

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

где new-это "размещение new"...

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

Comments

    Ничего не найдено.