当使用new实例化一个类时,如果不删除内存,通过重用对象可以获得哪些好处?
new的过程是什么?是否会发生上下文切换?分配新内存时由谁执行分配?操作系统?
当使用new实例化一个类时,如果不删除内存,通过重用对象可以获得哪些好处?
new的过程是什么?是否会发生上下文切换?分配新内存时由谁执行分配?操作系统?
您在这里提出了几个问题...
除了删除内存,基于对象的重用我们可以获得哪些好处?
这完全取决于您的应用程序。即使我知道应用程序是什么,您还未指定另一个细节 - 重用背后的策略是什么?但即使知道了这一点,很难预测或通用地回答。尝试一些方法并进行测量。
作为经验法则,我喜欢最小化最不必要的分配。虽然这主要是过早优化。只有在数千次调用中才会有所区别。
new的过程是什么?
完全依赖于实现。但分配器使用的一般策略是具有空闲列表,即已在进程中释放的块的列表。当空闲列表为空或包含不足的连续空闲空间时,它必须向内核请求内存,内核只能以固定页面大小的块分配内存。(x86上的4096)。分配器还必须决定何时切割、填充或合并块。多线程也可能对分配器施加压力,因为它们必须同步其空闲列表。
通常这是一个非常昂贵的操作。相对于您正在做的其他事情可能并不那么昂贵。但它不便宜。
是否发生上下文切换?完全有可能。也可能不会。您的操作系统可以在任何时候获得中断或系统调用时进行上下文切换,因此...这可能发生在很多时候;我没有看到这与您的分配器之间有任何特殊关系。
新内存是由谁分配的?操作系统?如果来自空闲列表,则无需涉及系统调用,因此无需操作系统的帮助。但如果空闲列表无法满足请求,则可能来自操作系统。此外,即使它来自空闲列表,您的内核也可能对该数据进行了页面处理,因此您可以在访问时遇到页错误,内核的分配器将启动。因此,我想这将是一个混合包。当然,您可以拥有符合规范的实现,这些实现可以做出各种疯狂的事情。
通常情况下,C++运行时使用操作系统的内存管理函数分配大块内存,然后使用自己的堆实现将其细分。Microsoft C++运行时大多使用Win32堆函数,这些函数在用户模式中实现,并使用虚拟内存API分配操作系统内存。因此,在当前分配的虚拟内存需要并且需要去操作系统分配更多内存之前,不会发生上下文切换。
在分配内存时存在一个理论问题,即如何找到可用块的堆遍历时间没有上限。但实际上,堆分配通常很快。
除了线程应用程序。由于大多数C++运行时在多个线程之间共享单个堆,因此需要对堆的访问进行串行化。这可能会严重影响某些依赖于多个线程能够新建和删除多个对象的应用程序类别的性能。
如果您使用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
}