在流行的实现中,C和C ++中的动态内存分配是否不同?

时间:2011-09-16 11:08:48

标签: c++ c memory-management

就各自的语言标准而言,C仅通过malloc()系列提供动态内存分配,而在C ++中,最常见的分配形式由::operator new()执行。 C风格的malloc也有C ++版本,许多“baby的第一个分配器”示例使用它作为其核心分配函数,但我很好奇当代编译器如何实现实际的生产操作符 - 新的。

它只是malloc()的一个瘦包装器,还是由于典型C ++程序与典型的C程序相比,内存分配行为有所不同,它是否实现了根本不同的实现?

[编辑:我认为主要区别通常描述如下:C程序具有更少,更大,更长寿的分配,而C ++程序具有许多小的短期分配。如果错误的话,请随意加入,但听起来考虑到这一点会让人受益。]

对于像GCC这样的编译器,只需要一个单独的核心分配实现并将其用于所有相关语言就很容易了,所以我想知道在每种语言中尝试优化分配性能的细节是否存在差异。


更新:感谢所有精彩的答案!看起来在GCC中,这完全由 ptmalloc 解决,并且MSVC也在核心使用malloc。有谁知道MSVC-malloc是如何实现的?

5 个答案:

答案 0 :(得分:48)

以下是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;
}

这可以在g ++ source distro中的libstdc++-v3/libsupc++/new_op.cc中找到。

正如您所看到的,它是malloc周围相当薄的包装。

编辑在许多系统上,可以通过调用malloc或设置环境变量来微调mallopt的行为。这是一个article讨论Linux上可用的一些功能。

According to Wikipediaglibc版本2.3+使用名为ptmalloc的分配器的修改版本,该版本本身是由Doug Lea设计的dlmalloc的衍生版本。有趣的是,在article about dlmalloc Doug Lea给出了以下观点(强调我的):

  

在编写了一些C ++之后,我编写了第一个版本的allocator   几乎完全依赖于分配动态内存的程序。   我发现他们跑的速度要慢得多,而且总得多得多   内存消耗比我预期的要多。这是由于   我正在运行的系统上的内存分配器的特性   on(主要是当时版本的SunOs和BSD)。反击   首先,我在C ++中编写了许多专用分配器,   通常通过为各种类重载operator new。一些   这些是在关于C ++分配技术的论文中描述的   适应1989年C ++报告文章的一些存储分配   容器类的技术。

     

然而,我很快就意识到为每个人构建一个特殊的分配器   倾向于动态分配和大量使用的新类是   在构建各种通用编程时,这不是一个好的策略   我当时正在写的支持班。 (从1986年到1991年,我是   libg ++的主要作者,GNU C ++库。)更广泛的   需要解决方案 - 编写一个足够好的分配器   在正常的C ++和C加载下,这样程序员就不会受到诱惑   编写专用分配器,除非非常特殊   条件。

     

本文介绍了一些主要的设计目标,   算法以及此分配器的实现注意事项。

答案 1 :(得分:15)

在大多数实施中operator new()只调用malloc()。实际上甚至是标准suggests that as a default stratege。当然,如果你想要更好的性能,你可以实现自己的operator new,通常用于一个类,但默认情况下通常只是调用malloc()

答案 2 :(得分:12)

glibc new运算符是malloc的一个薄包装器。 glibc malloc对不同的大小分配请求使用不同的策略。您可以查看实施,或至少查看评论here

以下是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.
*/

答案 3 :(得分:10)

在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 ++的new也包含malloc()电话。

答案 4 :(得分:2)

这不是性能问题: pA = new A具有与pA = (A*)malloc(sizeof(A));

不同的副作用

在第二个中,不调用A的构造函数。 要达到同样的效果,你应该做

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

其中new是“placement new”......

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