我有一个程序可以创建大量对象并将它们插入向量中。我的想法是创建大约 10.000 个对象,但我发现程序在几千个对象后崩溃。崩溃前创建的对象数量是随机的,取决于我是否修改代码中的任何行,所以我想与内存分配问题有关。
我正在创建的对象是这个:
class Object {
public:
//Needed by map
Object() {
}
Object(int newID, std::string newText) {
id = newID;
text = newText;
}
int getID() {
return id;
}
std::string getText() {
return text;
}
~Object() {
}
private:
int id;
std::string text;
};
如您所见,没什么特别的。创建对象的程序如下:
int main(int argc, char** argv) {
int numberOfElements;
long start;
long end;
long time1, time2, time3, time4, time5, time6;
numberOfElements = 7000; //7000<X<7050 Maximum reliable
{
//Measuring time for creation of 1000 elements in vector of objects,
cout << "VECTOR OF OBJECTS:" << endl;
start = getTimeInMicroseconds();
vector<Object> vectorOfObjects;
vectorOfObjects.reserve(10000);
for (int i = 0; i < numberOfElements; i++) {
cout << "Creating object " << i << endl;
Object object = *(new Object(i, "This is object "+i));
cout << "Created object " << i << endl;
vectorOfObjects.push_back(object);
cout << "Object inserted" << endl;
}
end = getTimeInMicroseconds();
time1 = end - start;
cout << "- Time to create " << numberOfElements << " objects = "
<< time1 << " microseconds" << endl;
}
return 0;
}
再次,非常简单的事情。崩溃之前创建的对象数量取决于我在此代码之后添加的内容。有时它在 2000 年之后崩溃,有时在 4000 之后崩溃,有时在 7000 之后崩溃......我想这是一个内存分配问题,但我不知道如何解决它。
我尝试将对象创建为:
Object object(i, "text");
vectorOfObjects.push_back(object);
vectorOfObjects.push_back(Object(i, "text");
vectorOfObjects.push_back(*(new Object(i, "text")));
但他们都没有奏效。当然,我更喜欢动态创建这些对象的方法,就像我在这里展示的最后两个示例一样;我也尝试了使用不同的容器,例如 map 或 deque,但由于问题是由于对象的创建而不是容器本身而发生的,因此我使用哪个容器并不重要。
这是核心转储:
-bash-3.2$ pstack core
core 'core' of 5884: ./datastructuresperformance
d147646c strlen (8046eb8, 8055000, 8046ebc, d17c34ed) + c
08051fdf main (1, 8047264, 804726c, 8051ddf) + f7
08051e27 _start (1, 80473b8, 0, 80473d4, 80473ef, 8047441) + 67
-bash-3.2$ pmap core
core 'core' of 5884: ./datastructuresperformance
08044000 16K rwx-- [ stack ]
08050000 20K r-x-- /export/home/dcs/SolStudioProjects/DataStructuresPerformance/dist/Release/OracleSolarisStudio-Solaris-x86/datastructuresperformance
08064000 8K rwx-- /export/home/dcs/SolStudioProjects/DataStructuresPerformance/dist/Release/OracleSolarisStudio-Solaris-x86/datastructuresperformance
08066000 280K rwx-- [ heap ]
D1450000 1088K r-x-- /lib/libc.so.1
D1560000 32K rwx-- /lib/libc.so.1
D1568000 8K rwx-- /lib/libc.so.1
D1570000 292K r-x-- /lib/libm.so.2
D15C8000 16K rwx-- /lib/libm.so.2
D15D0000 48K r-x-- /usr/lib/libCrun.so.1
D15EB000 8K rwx-- /usr/lib/libCrun.so.1
D15ED000 20K rwx-- /usr/lib/libCrun.so.1
D1600000 24K rwx--
D1610000 1244K r-x-- /usr/lib/libCstd.so.1
D1750000 4K rwx--
D1756000 216K rwx-- /usr/lib/libCstd.so.1
D1790000 4K rwx--
D17A0000 4K rw---
D17B0000 4K rw---
D17BF000 176K r-x-- /lib/ld.so.1
D17F0000 4K rwx--
D17FB000 8K rwx-- /lib/ld.so.1
D17FD000 8K rwx-- /lib/ld.so.1
total 3532K
这应该不是与内存量有关的问题,因为到目前为止该程序使用的最大内存量远低于这台机器的1GB。
-
*new T()
被称为内存泄漏运算符。您不想动态分配对象,相反,您只想构造一个对象(将与向量一起破坏) - 您不能使用
"string" + i
,因为它会在char(&)[7]
上执行指针算术(即"string"
)。从逻辑上讲,"string" + i
说&("string"[i])
,当i
比字符串文字的长度>时,这会导致未定义的行为(在您的情况下,崩溃)。
不要在C++中使用new
,除非你知道自己在做什么:
Object object = Object(i, "This is object " + to_string(i));
更好的是,如果您的编译器不是很旧,请考虑使用 emplace_back
:
vector<Object> vectorOfObjects;
vectorOfObjects.reserve(100000000ul);
for(int i = 0; i < numberOfElements; i++) {
vectorOfObjects.emplace_back(i, "This is object " + to_string(i));
}
(对不起,被那行吓了一跳:/)
Object object = *(new Object(i, "This is object "+i));
别这样。曾。试试这个:
Object object(i,"This is object");
这条线有 2 个问题:
a) 你实际做的是创建一个对象,复制它,然后忘记它。
b)(这是导致代码崩溃的事情) - 给它一个字符串,从比字符串"This is Object"向前i
字节的位置开始。你可以想象,这种记忆不是让你像这样自由阅读的。
除了已经给出的答案之外,我建议你保持简单。在大多数情况下,您不需要reserve
向量的大小,因为分配策略通常会提供出色的性能。以增量方式工作,分析代码,然后决定添加更多语句。
我之所以特别评论这一点,是因为这是一个更大疾病的症状:过早优化。
我使用以下代码进行测试:
#include<string>
#include<vector>
#include<iostream>
#include<boost/timer/timer.hpp>
class Object {
public:
Object() { }
Object(int id, const std::string& text)
: m_id(id),
m_text(text) { }
int id() const {
return m_id;
}
std::string text() const {
return m_text;
}
private:
int m_id;
std::string m_text;
};
int main(int argc, char* argv[]) {
size_t N = std::stoi(argv[1]); // add argc check
std::vector<Object> objects;
//objects.reserve(N);
{
boost::timer::auto_cpu_timer t;
for(size_t k=0; k<N; k++) {
//objects.emplace_back(Object(k, "random text"));
objects.push_back(Object(k, "random text"));
}
std::cout<<"Objects in container: "<<objects.size()<<std::endl;
}
}
对于我的机器中的优化代码(在OS X 10.7.4上使用GCC 4.8.1的g++ example.cpp -std=c++11 -O2
),我得到以下结果(N=1,000,000
的墙时间以秒为单位):
Push Emplace
Reserve 0.078 0.078
No Reserve 0.087 0.087
您可以继续开始定义移动构造函数,并优化您想要的所有内容,但一般来说,更简单的代码通常性能良好。