转自:https://blog.csdn.net/mfcing/article/details/8746256

一些好的公司校园招聘过程中(包括笔试、面试环节),经常会涉及到STL中vector的使用(主要是笔试)及其性能(面试)的分析。今天看了下相关文章,也写了几个小的测试程序跑了跑。算是总结下,希望对需要的人有帮助。

关于vector,简单地讲就是一个动态数组,里面有一个指针指向一片连续的内存空间,当空间不够装下数据时会自动申请另一片更大的空间,然后把原有数据拷贝过去,接着释放原来的那片空间;当释放或者说是删除里面的数据时,其存储空间并不会释放,仅仅只是清空了里面的数据。接下来,我会详细地说说这些。

备注:本文的相关程序都是在windows 7+VS2008环境下测试。

一、首先,看看vector的内存分配机制

vector<int> arr;
ofstream wf("1.txt");
for(int i=;i<;++i)
{
arr.push_back(i);
wf<<"capacity="<<arr.capacity()<<",size="<<arr.size()<<end;
}
wf.close();

capacity()返回的是当前vector对象缓冲区(后面的对vector维护的内存空间皆称为缓冲区)实际申请的空间大小,而size()返回的是当前对象缓冲区中存储数据的个数,capacity永远是大于等于size的,当size和capacity相等时继续添加数据时vector会扩容。

再来看看1.txt中的数据:

capacity=1,size=1
capacity=2,size=2
capacity=3,size=3
capacity=4,size=4
capacity=6,size=5
capacity=6,size=6
capacity=9,size=7
capacity=9,size=8
capacity=9,size=9
capacity=13,size=10
capacity=13,size=11
capacity=13,size=12
capacity=13,size=13
capacity=19,size=14
capacity=19,size=15
capacity=19,size=16
capacity=19,size=17
capacity=19,size=18
capacity=19,size=19
capacity=28,size=20
capacity=28,size=21
capacity=28,size=22
capacity=28,size=23
capacity=28,size=24
capacity=28,size=25
capacity=28,size=26
capacity=28,size=27
capacity=28,size=28
capacity=42,size=29
capacity=42,size=30
capacity=42,size=31
capacity=42,size=32
capacity=42,size=33
capacity=42,size=34
capacity=42,size=35
capacity=42,size=36
capacity=42,size=37
capacity=42,size=38
capacity=42,size=39
capacity=42,size=40
capacity=42,size=41
capacity=42,size=42
capacity=63,size=43
capacity=63,size=44
capacity=63,size=45
capacity=63,size=46
capacity=63,size=47
capacity=63,size=48
capacity=63,size=49
capacity=63,size=50
capacity=63,size=51
capacity=63,size=52
capacity=63,size=53
capacity=63,size=54
capacity=63,size=55
capacity=63,size=56
capacity=63,size=57
capacity=63,size=58
capacity=63,size=59
capacity=63,size=60
capacity=63,size=61
capacity=63,size=62
capacity=63,size=63
capacity=94,size=64
capacity=94,size=65
capacity=94,size=66
capacity=94,size=67
capacity=94,size=68
capacity=94,size=69
capacity=94,size=70
capacity=94,size=71
capacity=94,size=72
capacity=94,size=73
capacity=94,size=74
capacity=94,size=75
capacity=94,size=76
capacity=94,size=77
capacity=94,size=78
capacity=94,size=79
capacity=94,size=80
capacity=94,size=81
capacity=94,size=82
capacity=94,size=83
capacity=94,size=84
capacity=94,size=85
capacity=94,size=86
capacity=94,size=87
capacity=94,size=88
capacity=94,size=89
capacity=94,size=90
capacity=94,size=91
capacity=94,size=92
capacity=94,size=93
capacity=94,size=94
capacity=141,size=95
capacity=141,size=96
capacity=141,size=97
capacity=141,size=98
capacity=141,size=99
capacity=141,size=100

数据有点多,提炼下就是这样的:

capacity=1
capacity=2
capacity=3
capacity=4
capacity=6
capacity=9
capacity=13
capacity=19
capacity=28
capacity=42
capacity=63
capacity=94
capacity=141

看出其中的规律没?对,就是每次扩容都是增加当前空间的50%(第一次除外);

9+9/2=13;13+13/2=19;19+19/2=28……

其实STL的源码我们都可以看到的,具体就在你说安装的编译器目录下,例如,我的VS2008是在:安装目录\VC\include下面。你也可以在VS中直接选中#include <vector>右键打开。当然了,windows上的STL源码都是P.J. Plauger写的(PS:很牛B的博士,百度你就知道),大家都说可读性极差,我也这么认为,我们这些菜鸟还是看GCC中的STL源码吧。

\VC\include\vector中是这样扩容的:

if (_Count == )//这里进行了判断,但是什么都不做,不知道为什么???????
;
else if (max_size() - size() < _Count)//编译器可以申请的最大容量也装不下,抛出异常_THROW(length_error, "vector<T> too long");
_Xlen(); // result too long
else if (_Capacity < size() + _Count)//当前空间不足,需要扩容
{ // not enough room, reallocate
_Capacity = max_size() - _Capacity / < _Capacity
? : _Capacity + _Capacity / ; // try to grow by 50%,扩容50%
if (_Capacity < size() + _Count)//扩容50%后依然不够容下,则使容量等于当前数据个数加上新增数据个数
_Capacity = size() + _Count;
pointer _Newvec = this->_Alval.allocate(_Capacity);//申请新的空间
pointer _Ptr = _Newvec; _TRY_BEGIN
_Ptr = _Umove(_Myfirst, _VEC_ITER_BASE(_Where),
_Newvec); // copy prefix <span style="white-space:pre"> </span>//拷贝原有数据到新的内存中
_Ptr = _Ucopy(_First, _Last, _Ptr); // add new stuff<span style="white-space:pre"> </span>//拷贝新增数据到新的内存的后面
_Umove(_VEC_ITER_BASE(_Where), _Mylast, _Ptr); // copy suffix
_CATCH_ALL
_Destroy(_Newvec, _Ptr);
this->_Alval.deallocate(_Newvec, _Capacity);//释放原来申请的内存
_RERAISE;
_CATCH_END

对的,就是每次扩容50%。至于删除容器中数据的时候,缓冲区大小并不会改变,仅仅只是清除了其中的数据,只有在析构函数调用的时候vector才会自动释放缓冲区。

看看它的析构代码:

    ~vector()
{ // destroy the object
_Tidy();
}
void _Tidy()
{// free all storage
if (_Myfirst != )
{// something to free, destroy and deallocate it  #if _HAS_ITERATOR_DEBUGGING
this->_Orphan_all();
 #endif /* _HAS_ITERATOR_DEBUGGING */ _Destroy(_Myfirst, _Mylast);//应该是销毁vector中的每一个元素吧
this->_Alval.deallocate(_Myfirst, _Myend - _Myfirst);//释放缓冲区的空间
}
_Myfirst = , _Mylast = , _Myend = ;//指针全部归零
}

那么,我们可以在需要的时候强制释放缓冲区不?
答案是可以的,既然析构时会释放空间,那么我们就可以换个方式调用析构函数。

二、如何强制释放vector的缓冲区

    //方法一、
vector<int>().swap(arr); //交换后 //方法二、
{
vector<int> temp;//临时对象未初始化,其缓冲区大小为0,没有数据
arr.swap(temp);//与我们的对象交换数据,arr的缓冲区就没了。
}//临时变量会被析构,temp调用vector析构函数释放空间

三、如何使用提高性能

为了比较,我们用了三种方式来把100个数据存入vector中,分别是:1、直接每次push_back();2、使用resize()提前分配100个空间,然后push_back;3、使用reserve提前分配100个存储空间。MSDN中,这两个个函数的说明分别是:

reserve: Reserves a minimum length of storage for a vector object, allocating space if necessary.

resize:    Specifies a new size for a vector.

在这里我们初始化的时候使用感觉好像是差不多。

clock_t start=clock();
for(int num=;num<;++num)
{
vector<int> v1;
for(int i=;i<;++i)
v1.push_back(i);
}
cout<<"直接push循环10000次用时:"<<clock()-start<<endl;
start=clock();
for(int num=;num<;++num)
{
vector<int> v2;
v2.resize();
for(int i=;i<;++i)
v2.push_back(i);
}
cout<<"先resize预设大小再push循环10000次用时:"<<clock()-start<<endl;
start=clock();
for(int num=;num<;++num)
{
vector<int> v3;
v3.reserve();
for(int i=;i<;++i)
v3.push_back(i);
}
cout<<"先reserve预设大小再push循环10000次用时:"<<clock()-start<<endl;

结果如下所示

reserve只是保持一个最小的空间大小,而resize则是对缓冲区进行重新分配,里面涉及到的判断和内存处理比较多,当然了在这里由于最初都是空的所以差别不大。

两者的区别查看:vector::reserve和vector::resize的区别

由此可见,对于数据数目可以确定的时候,先预设空间大小是很有必要的。直接push_back数据频繁移动很是耗时(当然了,数据小的可以忽略的)。

整个程序的完整代码

#include "stdafx.h"
#include "btree.h"
#include <vector>
#include <iostream>
#include <Windows.h>
#include <fstream>
#include <time.h>
using std::ofstream;
using std::cout;
using std::endl;
using std::vector;
int _tmain(int argc, _TCHAR* argv[])
{
/************************************************************************/
/* vector如何强制释放内存空间 */
/* 默认只有析构时才会释放 */
/************************************************************************/
vector<int> arr;
cout<<"默认情况未初始化时,capacity="<<arr.capacity()<<endl;
arr.resize(,);
arr.reserve();
arr.resize();
cout<<"现在,capacity="<<arr.capacity()<<endl;
vector<int>::iterator itor=arr.begin()+;
arr.erase(arr.begin(),itor);
cout<<"capacity="<<arr.capacity()<<",size="<<arr.size()<<endl;
//方法一、
vector<int>().swap(arr); //强制释放空间
//方法二、
{
vector<int> temp;
arr.swap(temp);
}//临时变量会被析构
cout<<"capacity="<<arr.capacity()<<",size="<<arr.size()<<endl;
clock_t start=clock();
for(int num=;num<;++num)
{
vector<int> v1;
for(int i=;i<;++i)
v1.push_back(i);
}
cout<<"直接push循环10000次用时:"<<clock()-start<<endl;
start=clock();
for(int num=;num<;++num)
{
vector<int> v2;
v2.resize();
for(int i=;i<;++i)
v2[i] = i;
}
cout<<"先resize预设大小再push循环10000次用时:"<<clock()-start<<endl;
start=clock();
for(int num=;num<;++num)
{
vector<int> v3;
v3.reserve();
for(int i=;i<;++i)
v3.push_back(i);
}
cout<<"先reserve预设大小再push循环10000次用时:"<<clock()-start<<endl;
vector<int> v4;
ofstream wf("2.txt");
int nFlag=v4.capacity();
for(int i=;i<;++i)
{
v4.push_back(i);
if(nFlag!=v4.capacity())
{
nFlag=v4.capacity();
cout<<"new buffer size="<<nFlag<<endl;
wf<<"capacity="<<nFlag<<endl;
}
}
wf.close();
cout<<"max_size="<<arr.max_size()<<endl;
return ;
}

【C++】vector内存机制和性能分析的更多相关文章

  1. C++ STL中vector的内存机制和性能分析

    vecotr是动态数组,顾名思义他可以动态的增加自己的长度. 内存机制: 但是怎样的增加自己的长度? vector有两个函数一个是capacity()返回内存空间即缓冲区的大小,另一个是size()返 ...

  2. jstack(查看线程)、jmap(查看内存)和jstat(性能分析)

    公司内部同事分享的一篇文章 周末看到一个用jstack查看死锁的例子.昨天晚上总结了一下jstack(查看线程).jmap(查看内存)和jstat(性能分析)命令.供大家参考 1.Jstack 1.1 ...

  3. jstack(查看线程)、jmap(查看内存)和jstat(性能分析)命令

    jstack(查看线程).jmap(查看内存)和jstat(性能分析)命令   公司内部同事分享的一篇文章 周末看到一个用jstack查看死锁的例子.昨天晚上总结了一下jstack(查看线程).jma ...

  4. 命令:jstack(查看线程)、jmap(查看内存)和jstat(性能分析)命令

    命令:jstack(查看线程).jmap(查看内存)和jstat(性能分析)命令 这些命令 必须 在 linux jdk bin 路径 下执行 eq: ./jstack 10303 即可  如果想把 ...

  5. SQL Server ->> Memory Allocation Mechanism and Performance Analysis(内存分配机制与性能分析)之 -- Minimum server memory与Maximum server memory

    Minimum server memory与Maximum server memory是SQL Server下配置实例级别最大和最小可用内存(注意不等于物理内存)的服务器配置选项.它们是管理SQL S ...

  6. valgrind和Kcachegrind性能分析工具详解

    一.valgrind介绍 valgrind是运行在Linux上的一套基于仿真技术的程序调试和分析工具,用于构建动态分析工具的装备性框架.它包括一个工具集,每个工具执行某种类型的调试.分析或类似的任务, ...

  7. .NET内存性能分析宝典

    .NET Memory Performance Analysis 知道什么时候该担心,以及在需要担心的时候该怎么做 译者注 **作者信息:Maoni Stephens ** - 微软架构师,负责.NE ...

  8. Android内存机制分析1——了解Android堆和栈

    //----------------------------------------------------------------------------------- Android内存机制分析1 ...

  9. SQL Server内存性能分析

    内存概念: Working Set = Private Bytes + Shared Memory Working Set:某个进程的地址空间中,存放在物理内存的那一部分 Private Bytes: ...

随机推荐

  1. Linux 独立启动方式安装 Archiva

    为了方便起见,我们假设你的 archiva 安装到目录 /opt 下面. 下载安装程序 进入 Archiva 的项目的下载页面中,请单击链接:https://archiva.apache.org/do ...

  2. 7.4 GRASP原则四:控制器 Controller

    4.GRASP原则四:控制器 Controller  What first object beyond the UI layer receives and co-ordinates (control ...

  3. es6学习---.babelrc文件

    babel是用来进行转码的,在不支持es6的环境下,需要将es6的语法转码成es5的语法格式,就用到了babel. .babelrc 文件的配置 在项目的根目录下创建 .babelrc 文件 文件包括 ...

  4. redis的数据类型及使用

    Redis 数据类型 Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合). String(字符串) st ...

  5. 谈谈Java中的volatile(转载)

    内存可见性 留意复合类操作 解决num++操作的原子性问题 禁止指令重排序 总结 内存可见性 volatile是Java提供的一种轻量级的同步机制,在并发编程中,它也扮演着比较重要的角色.同synch ...

  6. Python ORM框架之SQLAlchemy

    前言: Django的ORM虽然强大,但是毕竟局限在Django,而SQLAlchemy是Python中的ORM框架: SQLAlchemy的作用是:类/对象--->SQL语句--->通过 ...

  7. centos 安装composer

    1 下载composer.phar curl -sS https://getcomposer.org/installer | php 2 设置全局调用 mv composer.phar /usr/lo ...

  8. sql查询练习

    #建学生信息表student create table student ( sno varchar(20) not null primary key, sname varchar(20) not nu ...

  9. Linux shell基础知识(上)

    Linux shell基础知识(上) 目录 一.shell介绍 二.命令历史 三.命令补全和别名 四.通配符 五.输入输出重定向 六.管道符和作业控制 七.shell变量 八.环境变量配置文件 九.b ...

  10. Java RedisClient

    package org.rx.util; import org.redisson.Redisson; import org.redisson.api.RedissonClient; import or ...