C++ 矢量增加峰值存储器

C++ 矢量增加峰值存储器,c++,vector,C++,Vector,这是我最后一个问题的继续。我无法理解vector占用的内存。问题框架: 考虑一个向量,它是列表的集合,列表是指针的集合。一模一样: std::vector<std::list<ABC*> > vec; 此时,向量的容量和大小将为613284686。正确的。调整大小后,我将在相应索引处插入列表,如下所示: // Some where down in the program, make these lists. Simple push for now. std::list&

这是我最后一个问题的继续。我无法理解vector占用的内存。问题框架:

考虑一个向量,它是列表的集合,列表是指针的集合。一模一样:

std::vector<std::list<ABC*> > vec;
此时,向量的容量和大小将为613284686。正确的。调整大小后,我将在相应索引处插入列表,如下所示:

// Some where down in the program, make these lists. Simple push for now.
std::list<ABC*> l1;
l1.push_back(<pointer_to_class_ABC>);
l1.push_back(<pointer_to_class_ABC>);

// Copy the list at location
setInfo(613284686, l1);

void setInfo(uint64_t index, std::list<ABC*> list>) {
  std::copy(list.begin(), list.end(), std::back_inserter(vec.at(index));
}
//在程序的下面,列出这些列表。现在只需简单的推动。
std::列表l1;
l1.推回();
l1.推回();
//复制位置处的列表
setInfo(613284686,l1);
void setInfo(uint64\u t索引,标准::列表>){
std::copy(list.begin()、list.end()、std::back_插入器(vec.at(index));
}
好的。插入完成了。值得注意的是:

矢量大小为:613284686 向量中的条目是:3638243731//通过遍历向量索引并在每个索引处添加std::list的大小来计算

现在,由于有3638243731个指针条目,我希望这个向量占用的内存是~30Gb。3638243731*8(字节)=~30Gb

但当我在内存中有这些数据时,内存峰值达到400G

然后我用以下方法清除这个向量:

std::vector<std::list<nl_net> >& ccInfo = getVec(); // getVec defined somewhere and return me original vec.
std::vector<std::list<nl_net> >::iterator it = ccInfo.begin();
for(; it != ccInfo.end(); ++it) {
  (*it).clear();
}

ccInfo.clear(); // Since it is an reference
std::vector<std::list<nl_net> >().swap(ccInfo); // This makes the capacity of the vector 0.
std::vector&ccInfo=getVec();//在某处定义了getVec,并将原始vec返回给我。
std::vector::iterator it=ccInfo.begin();
for(;it!=ccInfo.end();++it){
(*it.clear();
}
ccInfo.clear();//因为它是一个引用
std::vector().swap(ccInfo);//这使向量的容量为0。
清除这个向量后,内存会下降到100G,这对一个向量来说太多了

你们能纠正我在这里没有理解的地方吗

顺便说一句,我不能在较小的情况下复制它,它将在我的项目中出现

此时,向量的容量和大小将为613284686

至少是613284686,可能更多

std::vector().swap(ccInfo);//这使向量的容量为0。
从技术上讲,标准并不能保证默认构造的向量不会有0以外的容量……但在实践中,这可能是正确的

现在,由于有3638243731个指针条目,我希望这个向量占用的内存是~30Gb。3638243731*8(字节)

但是向量不包含指针。它包含
std::list
对象。因此,您应该期望向量本身的缓冲区使用
vec.capacity()*sizeof(std::list)
字节。每个列表至少有一个指向开始和结束的指针

此外,您应该期望每个列表中的每个元素也使用内存。因为列表是双链接的,所以您应该期望每个元素大约有两个指针加上数据(第三个指针)内存

此外,列表中的每个指针显然都指向一个
ABC
对象,并且每个指针都使用
sizeof(ABC)
内存

此外,由于链表的每个元素都是单独分配的,并且每个动态分配都需要簿记,以便可以单独取消分配,并且每个分配必须与最大本机对齐对齐,并且空闲存储在执行过程中可能会出现碎片,因此将存在大量与wi相关的开销每个动态分配

清除这个向量后,内存下降到100G

语言实现通常会保留从操作系统分配的(部分)内存。如果您的目标系统记录了一个实现特定的函数,用于显式请求释放此类内存,那么您可以尝试使用该函数

但是,如果向量缓冲区不是最新的动态分配,那么它的释放可能会在空闲存储中留下大量可重用区域,但是如果以后存在分配,那么所有内存可能无法释放回操作系统

即使langauge实现已将内存释放给操作系统,操作系统通常会为进程映射内存,直到另一个进程实际需要内存用于其他用途。因此,根据您测量内存使用的方式,结果可能不一定有意义


可能有用的一般经验法则:

  • 除非使用所有(或大部分)索引,否则不要使用向量。如果不使用,则考虑稀疏数组(尽管没有这样的数据结构的标准容器)。
  • 使用vector时,如果知道分配的上限,请在调整大小之前保留
  • 没有充分的理由不要使用链表
  • 不要依赖于从峰值使用中恢复所有内存(回到操作系统;内存仍然可以用于进一步的动态分配)
  • 不要强调虚拟内存的使用

std::list是一个碎片内存容器。通常每个节点都必须有它正在存储的数据,加上2个上/下指针,然后您必须在OS分配表中添加所需的空间(通常每个分配16或32字节-取决于OS)。然后,您必须考虑以下事实:所有分配必须在16字节边界上返回(无论如何,在基于Intel/AMD的64位计算机上)

因此,以
std::list
为例,指针的大小是8,但是您至少需要48字节来存储每个元素(至少)

因此,仅列表项的内存使用量大约为:3638243731*48(字节)=~162Gb。 当然,这是假设没有内存碎片(可能有一个62字节的可用块,操作系统返回整个62字节的块,而不是请求的48字节)。这里我们还假设操作系统的最小分配大小为48字节(不用说64字节,这不会太傻,但会让使用率提高很多)

向量中std::list本身的大小约为18GB
std::vector<std::list<nl_net> >& ccInfo = getVec(); // getVec defined somewhere and return me original vec.
std::vector<std::list<nl_net> >::iterator it = ccInfo.begin();
for(; it != ccInfo.end(); ++it) {
  (*it).clear();
}

ccInfo.clear(); // Since it is an reference
std::vector<std::list<nl_net> >().swap(ccInfo); // This makes the capacity of the vector 0.
vec.resize(613284686);
std::vector<std::list<nl_net> >().swap(ccInfo); // This makes the capacity of the vector 0.
ccInfo.clear();
ccInfo.shrinkToFit();