development

동적 메모리 할당은 널리 사용되는 구현에서 C와 C ++에서 다릅니 까?

big-blog 2020. 12. 30. 20:15
반응형

동적 메모리 할당은 널리 사용되는 구현에서 C와 C ++에서 다릅니 까?


각 언어 표준이 진행되는 한 C는 malloc()제품군을 통해서만 동적 메모리 할당을 제공 하는 반면 C ++에서는 가장 일반적인 할당 형식이 ::operator new(). C 스타일 malloc은 C ++에서도 사용할 수 있으며, 많은 "아기의 첫 번째 할당 자"예제에서이를 핵심 할당 함수로 사용하지만 현대 컴파일러가 실제 생산 연산자를 어떻게 구현하는지 궁금합니다.

그것은 단지 얇은 래퍼입니까 malloc(), 아니면 일반적인 C 프로그램과 비교하여 일반적인 C ++ 프로그램의 메모리 할당 동작이 다소 다르기 때문에 근본적으로 다르게 구현됩니까?

[ 편집 : 주요 차이점은 일반적으로 다음과 같이 설명됩니다. AC 프로그램은 더 적고 더 크고 수명이 긴 할당을 가지고있는 반면 C ++ 프로그램은 많은, 작고, 짧은 할당을 가지고 있습니다. 그것이 틀렸다면 부담없이 들으십시오. 그러나 이것을 고려하면 도움이 될 것 같습니다.]

GCC와 같은 컴파일러의 경우 단일 코어 할당 구현을 하나만 가지고 모든 관련 언어에 사용하는 것이 쉬울 것이므로 각 언어에서 결과 할당 성능을 최적화하려는 세부 사항에 차이가 있는지 궁금합니다.


업데이트 : 모든 훌륭한 답변에 감사드립니다! GCC에서 이것은 ptmalloc의해 완전히 해결되고 MSVC도 malloc코어에서 사용하는 것처럼 보입니다 . MSVC-malloc이 어떻게 구현되는지 아는 사람이 있습니까?


다음은에서 사용하는 구현입니다 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.ccg ++ 소스 배포판 내부에서 찾을 수 있습니다.

보시다시피, 그것은 주위에 상당히 얇은 래퍼 malloc입니다.

편집 많은 시스템 malloc에서 일반적으로 mallopt환경 변수를 호출 하거나 설정 하여의 동작을 미세 조정할 수 있습니다 . 다음은 Linux에서 사용할 수있는 일부 기능에 대해 설명하는 기사 입니다.

위키 백과에 따르면 , glibc버전이라는 할당의 수정 된 버전 사용 2.3 ptmalloc자체의 파생, dlmalloc에 의해 설계된 더그 레아를 . 흥미롭게도 Doug Lea 에 대한 기사dlmalloc 에서 다음과 같은 관점을 제공합니다 (강조).

동적 메모리 할당에 거의 독점적으로 의존하는 일부 C ++ 프로그램을 작성한 후 할당 자의 첫 번째 버전을 작성했습니다. 나는 그들이 내가 예상했던 것보다 훨씬 더 느리게 그리고 / 또는 훨씬 더 많은 총 메모리 소비로 실행된다는 것을 발견했습니다. 이것은 내가 실행중인 시스템 (주로 SunO 및 BSD의 당시 현재 버전)의 메모리 할당 자의 특성 때문이었습니다. 이에 대응하기 위해 처음에는 다양한 클래스에 대해 new operator를 오버로딩하여 C ++로 여러 특수 목적 할당자를 작성했습니다. 이들 중 일부는 1989 년 C ++ 보고서 기사 컨테이너 클래스에 대한 일부 스토리지 할당 기술에 채택 된 C ++ 할당 기술에 대한 문서에 설명되어 있습니다.

그러나 나는 그 당시에 작성하고 있던 종류의 범용 프로그래밍 지원 클래스를 빌드 할 때 동적으로 할당되고 많이 사용되는 경향이있는 각각의 새 클래스에 대한 특수 할당자를 빌드하는 것이 좋은 전략이 아니라는 것을 곧 깨달았습니다. (1986 년부터 1991 년까지 저는 GNU C ++ 라이브러리 인 libg ++의 주 저자였습니다.) 프로그래머가 유혹에 빠지지 않도록 정상적인 C ++ 및 C로드에서 충분히 좋은 할당자를 작성 하려면 더 광범위한 솔루션이 필요 했습니다. 매우 특수한 조건을 제외하고 특수 목적 할당자를 작성하십시오.

이 기사에서는이 할당 자에 대한 몇 가지 주요 설계 목표, 알고리즘 및 구현 고려 사항에 대한 설명을 제공합니다.


대부분의 구현에서 operator new()바로 호출합니다 malloc(). 사실 표준조차도 그것을 기본 전략으로 제안합니다 . 물론 operator new더 나은 성능을 원하면 일반적으로 클래스에 대해 자체를 구현할 수 있지만 기본값은 일반적으로 malloc().


glibc new operator는 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 ++ newmalloc()호출을 래핑합니다 .


성능의 문제가 아닙니다. 다음 pA = new A과 다른 부작용이 있습니다.pA = (A*)malloc(sizeof(A));

In the second one, the A's constructor is not called. To come to a same effect you should do

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

where new is the "placement new"...

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

ReferenceURL : https://stackoverflow.com/questions/7443782/does-dynamic-memory-allocation-differ-in-c-and-c-in-popular-implementations

반응형