2

我需要澄清一个我不太明白的问题。使用以下两种情况,我会认为分配的内存量大致相同。但是,场景 2bad_alloc在一段时间后给了我一个异常,并且似乎在疯狂地消耗内存(使用 Window 的任务管理器作为分配给进程的内存量的代理)。以下是在 Windows 32bit 上使用 MSVC10 编译的。

假设我有以下基类:

template<class T>
class Base
{
protected:
    T x;
public:
    Base() {}
    Base(T _x) : x(_x){}
    virtual bool func() = 0;
};

现在,至于派生类:

template<class T>
class Derived : public Base<T>
{
public:
    Derived() {}
    Derived(T _x) :  Base(_x){}
    bool func() { return true; };
};

现在考虑两种情况。首先,分配一个 Derived 类的动态数组并用 Derived 对象填充它,即:

int main()
{
    int size = SOME_LARGE_NUMBER;
    Derived<int>* d = new Derived<int>[size];
    for (int i = 0; i < size; i++)
    {
        d[i] = Derived<int>(i);
    }

    // delete here
}

其次,分配 Base 类的动态指针数组,并让它们指向 Derived 类的实际实例,即:

int main()
{
    int size = SOME_LARGE_NUMBER;
    Base<int>** d = new Base<int>*[size];
    for (int i = 0; i < size; i++)
    {
        d[i] = new Derived<int>(i);
    }

    // delete here
}

在任一情况下,我都将 SOME_LARGE_NUMBER 设置为 40,000,000。在第一个场景中,程序完成得很好——在第二个场景中,我得到了一个 bad_alloc 异常。我想知道这是否是预期的行为,或者我是否在这里忽略了某些东西?如果是这样,这样做的更好方法是什么?请注意,我使用vector<Base<int>*>and遇到了同样的问题boost::ptr_vector<Base<int>>

4

5 回答 5

6

好吧,如果你在一个块中分配,它需要size数倍的大小Derived和单个分配的小开销。但是如果单独分配,则需要size乘以指针大小(4 字节),再加size上乘以大小Derivedsize(加一)乘以分配开销,即至少 8 字节。如果您靠近边缘,那么 12 倍size的额外字节很可能是仍然适合内存与不适合内存之间的区别。

我不会肯定地告诉您分配器开销正好是 8 个字节,但是使它少于 2 个指针会非常复杂,而且 Microsoft 标准分配器不太可能那么复杂。

请注意,由于std::vectorboost::ptr_vector执行与手动操作完全相同的想法,因此它们以相同的方式失败。它们只是确保在适当的地方调用 delete 的包装器,仅此而已。

对于 64 位目标,所有开销都将翻倍。那里的指针是 8 个字节,分配器开销通常是 2 个指针。

于 2011-04-18T12:05:19.420 回答
4

在第二个示例中,您分配内存来存储指向对象的指针和对象本身,它们本身至少使用:

(sizeof(Base<int>*)+sizeof(Derived<int>))*size

根据您平台上的指针大小,与第一个示例相比,这可能会导致相当大的开销。

像第二个例子那样动态地单独分配每个对象也可能是浪费的,因为运行时只能提供与 sizeof(Derived<int>) 严重匹配的某些大小的内存块。

于 2011-04-18T12:05:20.660 回答
0

我认为在第一种情况下,您在一个块中没有足够的内存。在第二种情况下,您有足够的内存用于指针数组,并有足够的内存为对象分配内存中的不同位置/块。

于 2011-04-18T11:42:04.843 回答
0

编辑:我没有很好地阅读代码,似乎第二个示例中的开销是双内存分配,一个用于指针,另一个用于对象本身:)

于 2011-04-18T11:47:51.450 回答
0

我看不出这两种实现有什么明显的错误(也许我今天早上没有喝足够的咖啡。)我建议在调试器中查看分配的内存,看看发生了什么。例如,如果它在示例 2 中的相同分配项处崩溃,那么很可能您的内存不足。(这没有解释为什么第一个实现有效而第二个无效,但它至少会让你走上正确的道路。)

您还可以比较调试器中分配的内存块大小,并确保它们在分配时实际上是相同的大小。

对不起,我不能提供更多。

于 2011-04-18T11:59:05.277 回答