Отличается ли динамическое выделение памяти в C и C++ в популярных реализациях?
что касается соответствующих языковых стандартов, C предлагает динамическое выделение памяти только через malloc() семья, в то время как в C++ наиболее распространенная форма выделения выполняется ::operator new(). C-style malloc также доступен на C++, и многие примеры "первого распределителя ребенка" используют его в качестве основной функции распределения, но мне любопытно, как современные компиляторы реализуют фактический оператор производства-новый.
это просто тонкая обертка вокруг malloc(), или это реализовано принципиально по-разному из-за довольно различного поведения выделения памяти типичной программы на C++ по сравнению с типичной программой на C?
[Edit: Я считаю, что основное различие обычно описывается следующим образом: программа C имеет меньше, больше, длительные распределения, в то время как программа C++ имеет много, небольшие, кратковременные распределения. Не стесняйтесь перезвонить, если это ошибочно, но похоже, что было бы полезно принять это во внимание счет.]
для компилятора, такого как GCC, было бы легко иметь только одну реализацию выделения ядра и использовать ее для всех соответствующих языков, поэтому мне интересно, есть ли различия в деталях, которые пытаются оптимизировать результирующую производительность выделения на каждом языке.
обновление: Спасибо за хорошие ответы! Похоже, в GCC это полностью решается с помощью ptmalloc, и что MSVC также использует malloc в ядро. Кто-нибудь знает, как реализуется MSVC-malloc?
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