用户空间内存碎片 让我们假设一个非常基础的C++程序,分配了大量的小STD::向量。 我真的不知道编译器和操作系统将如何将这些向量放置在进程内存空间中,但如果这个数字很大,我认为一些向量可能很接近
现在,假设我删除了内存中的一些向量,并保留了一些其他向量。 假设我想向第一个向量添加10000项 如果第二个向量在内存中太近,会发生什么?用户空间内存碎片 让我们假设一个非常基础的C++程序,分配了大量的小STD::向量。 我真的不知道编译器和操作系统将如何将这些向量放置在进程内存空间中,但如果这个数字很大,我认为一些向量可能很接近,c++,c++11,C++,C++11,现在,假设我删除了内存中的一些向量,并保留了一些其他向量。 假设我想向第一个向量添加10000项 如果第二个向量在内存中太近,会发生什么? 您认为我会出现“内存不足”错误,还是操作系统应该移动第一个向量?不,向量是否彼此接近并不重要。只有当向量达到某个大小时,没有连续的内存块可以容纳它的内存,您才会得到一个错误(对于默认的分配器,将抛出一个std::bad_alloc异常) 内部发生的情况与移动的含义类似,但在C++11中,该术语有不同的含义,因此我将尽量避免这种情况,而宁愿将其称为重新分配。还
您认为我会出现“内存不足”错误,还是操作系统应该移动第一个向量?不,向量是否彼此接近并不重要。只有当向量达到某个大小时,没有连续的内存块可以容纳它的内存,您才会得到一个错误(对于默认的分配器,将抛出一个
std::bad_alloc
异常)
内部发生的情况与移动的含义类似,但在C++11中,该术语有不同的含义,因此我将尽量避免这种情况,而宁愿将其称为重新分配。还要注意,操作系统与此无关
让我们看看细节:
一个std::vector
是连续的,这是正确的,但是(与std::array
相反),它的元素不直接存储在std::vector
实例本身中。相反,它将底层数组存储在堆上,并且只保留一个指向它的指针
出于效率原因,允许实现使其内部数组大于数组中存储的元素数。例如:
std::vector<int> v;
assert(v.size() == 0);
assert(v.capacity() >= 0); // at least v.size(), but can be higher
std::vector v;
断言(v.size()==0);
断言(v.capacity()>=0);//至少为v.size(),但可以更高
将新元素添加到向量时(例如,通过v.push_back
),将发生以下两种情况之一:
- 如果有足够的空间(即
),则可以添加新元素,而无需任何额外的内存分配v.size()
- 否则,必须增加基础阵列,这涉及以下步骤:
- 将分配一个新的(更大的)阵列
- 必须将旧数组中的所有元素复制到新数组中
- 新数组替换旧数组(将被释放),您可以插入新元素
std::vector
实例本身将保持在相同的内存地址,现在只有它的内部指针指向新创建的(更大的)数组。在这方面,数据已被移动到不同的内存位置。(这也会产生后果,例如,保留到元素的所有迭代现在都无效。)
关键的操作是重新分配内存。在这里,内存碎片开始发挥作用。可能会发生这样的情况,即由于碎片,即使没有碎片也有足够的空间,也无法分配新阵列
与其他语言不同,C++不可能在压缩垃圾回收器的方式中避免碎片化(例如,java中的一些GC实现正在压缩)。以同样的方式,操作系统无法帮助避免C++中的内存碎片。至少在理论上是这样。实际上,在今天的64位系统(具有虚拟内存)中,内存碎片不像过去那样令人担忧
如果不需要容器中的元素必须是连续的属性,可以使用
std::dequeue
而不是std::vector
。它对内存碎片更具鲁棒性,因为它不会保留一个大数组,而是几个较小的块。另一方面,std::vector
通常效率更高,因此默认情况下我仍然会使用该向量,但Herb Sutter的一篇老文章触及了这个主题:当您的std::vector
容量不足时,它会重新分配空间(通常2*所需大小
,请参阅分摊的复杂性)并移动向量中已有的元素。它将在第一个向量内移动数据指针,而不会移动向量本身(向量和向量数据位于不同的位置)
您的std::vector
和“内部”元素通常不在同一位置。这种不完整的伪实现是错误的,原因有很多,但可能说明push_back
如何在内部扩展:
namespace std {
template<typename T>
class vector<T>
size_t size_;
size_t capacity_;
T* data_; // Stored elsewhere on the heap.
void push_back(const T& foo) {
if (size_ == capacity_) {
capacity_ *= 2; // assuming capacity_ > 0, and non-wrapping size
data_ = realloc(data_, capacity_ * sizeof(T)); // assumes POD types and no realloc failures.
}
data_[++size_] = foo;
}
}
}
如果我理解了你的要求——向量不一定按顺序放在内存中。除非在物理上没有空间放置,否则您永远不会耗尽分配空间。这种操作的机制是这样的:如果您大幅增加第一个向量的大小,使其容量必须增加,则该过程会将向量的内容移动到临时向量数组。然后,新容量的新向量将在一个有足够的连续内存容纳它的地方创建。然后,临时数组的内容将被移动到新的较大向量,临时数组将被销毁。你永远不会因为两个向量太“接近”而让它们彼此“碰撞”。当你说“内存中太接近”时,你是指物理RAM中吗?或者你的意思是在进程的虚拟地址空间中?你的问题背后可能有一些困惑。
int main() {
std::vector<float> numbers; // the vector is on the stack and never moves.
numbers.push_back(5.0f);
// 5.0f is stored inside vector data, which may be on the heap.
// Adding more items may allocate heap memory and move all previous items.
return 0;
}