C++高效地使用新运算符
你已经问了几个问题在这里...
不用删除,我们将获得基于对象的重用什么样的好处记忆的?
这完全取决于您的应用程序。即使假设我知道应用程序是什么,您也没有说明另一个细节 - 重用的策略是什么?但即使知道,一般很难预测或回答。尝试一些东西并测量它们。
作为一个经验法则,我喜欢以尽量减少最为无偿分配。不过,这大多是不成熟的优化。它只会对数千次呼叫产生影响。
什么是新的进程?
完全依赖执行。但是分配器使用的一般策略是有一个空闲列表,也就是在这个过程中被释放的块列表。当空闲列表为空或连续空闲空间不足时,它必须向内核请求内存,它只能以恒定页面大小的块发出。 (在x86上是4096)。分配器还必须决定何时切割,填充或合并块。多线程也会对分配器施加压力,因为它们必须同步它们的空闲列表。
一般来说这是一个非常昂贵的操作。也许与其他你所做的不相关。但它并不便宜。
是否发生上下文切换?完全可能。它也可能不会。任何时候你的操作系统都可以*地进行上下文切换,所以当你得到一个中断或系统调用时,呃...这可能发生在很多时候;我没有看到这与你的分配器之间有任何特殊的关系。
分配了新的内存,谁在进行分配? OS?它可能来自一个空闲列表,在这种情况下不涉及系统调用,因此没有OS的帮助。但是,如果空闲列表无法满足请求,它可能来自操作系统。此外,即使它来自空闲列表,您的内核可能已经分页出了这些数据,因此您可能会在访问时遇到页面错误,并且内核的分配器将会启动。所以我想它会是一个混杂的包。当然,你可以有一个符合实现,做各种疯狂的事情。
- new为堆上的类分配内存,并调用构造函数。
- 上下文切换不必发生。
- C++运行时使用它认为合适的任何机制在其freestore上分配内存。
通常,C++运行时使用操作系统内存管理函数分配大块内存,然后使用自己的堆实现细分这些内存块。微软的C++运行时大多使用在用户模式下实现的Win32堆函数,并分配使用虚拟内存apis分配的操作系统内存。因此,直到并且除非它需要虚拟内存的当前分配并且需要去操作系统分配更多的内容,否则没有上下文切换。
分配内存时存在一个堆遍历可能会花费多长时间找到一个空闲块没有上限有一个理论问题。实际上,堆分配通常很快。
随着线程应用程序的例外。因为大多数C++运行时在多个线程之间共享一个堆,所以需要对堆进行序列化。这可能会严重降低某些类应用程序的性能,这些应用程序依赖于多个线程可以新建和删除多个对象。
glibc有一个分配器,它在多线程应用程序中不会降级。 – 2011-01-06 10:57:43
如果new
或delete
地址标记为已占据或未分配。这些实现不会一直与内核进行通信。更大的内存卡盘被保留,并分成更小的卡盘在您的应用程序中的用户空间。
因为new
和delete
是重入(或线程安全取决于实施)可能会发生上下文切换,但您的实现是线程安全的,而无论如何使用默认new
和delete
。
在C++中,您可以覆盖new
和delete
运算符,例如,把你的内存管理:
#include <cstdlib> //declarations of malloc and free
#include <new>
#include <iostream>
using namespace std;
class C {
public:
C();
void* operator new (size_t size); //implicitly declared as a static member function
void operator delete (void *p); //implicitly declared as a static member function
};
void* C::operator new (size_t size) throw (const char *){
void * p = malloc(size);
if (p == 0) throw "allocation failure"; //instead of std::bad_alloc
return p;
}
void C::operator delete (void *p){
C* pc = static_cast<C*>(p);
free(p);
}
int main() {
C *p = new C; // calls C::new
delete p; // calls C::delete
}
使用比标准之外的其它分配器,应根据分析结果或者通过设计(如内存池)。除此之外,在大多数情况下,实现自带的分配器都不会有问题。 – AraK 2011-01-06 08:37:21
的确是个好问题。我有一种情况,在分析后,我切换到了许多小而恒定大小的对象的池分配器。我认为一个上下文切换只发生在一次分配多个页面(在Linux上使用mmap),这些页面在分配器的内部被重用。现在,如果事先知道您分配的对象都具有相同的大小,则会有不同的分配策略。标准库分配器针对一般情况进行了调整,而不是特定的情况。我听说有些分配器为小型,中型和大型对象维护几个内存池。 – 2011-01-06 09:33:54