【问题标题】:Why does std::vector::operator= overallocate memory?为什么 std::vector::operator= 过度分配内存?
【发布时间】:2019-04-13 16:15:26
【问题描述】:

我正在调查一个消耗的内存超出需要的情况。如果我将字符串分配给std::vector,它会突然保留比所需更多的堆内存,即使字符串的大小已知:

以下是我将其分解为:

#include <vector>
#include <iostream>
#include <new>

void* operator new(size_t size) { 
    void * p = malloc(size); 
    std::cout << "\talloc " << size << " @ " << p; 
    return p;
} 

void operator delete(void* p) { 
    std::cout << "\t      free " << p; 
    free(p);
} 

int main() {
    {
        std::cout << std::endl << "1. Create first string:   ";
        auto s1 = std::string{"String with 20 chars"};

        std::cout << std::endl << "2. Create longer string:  ";
        auto s2 = std::string{"String with 25 characters"};

        std::cout << std::endl << "3. Copy construct:        ";
        auto s3 = s2;

        std::cout << std::endl << "4. Copy assign:           ";
        s1 = s3;

        std::cout << std::endl << "5. Leaving scope:         ";
    }
    std::cout << std::endl;
}

结果:

1. Create first string:     alloc 21 @ 0x56047f176280
2. Create longer string:    alloc 26 @ 0x56047f1762a0
3. Copy construct:          alloc 26 @ 0x56047f1762d0
4. Copy assign:             alloc 41 @ 0x56047f176300         free 0x56047f176280
5. Leaving scope:                 free 0x56047f1762d0         free 0x56047f1762a0         free 0x56047f176300

我希望第 4 行与第 3 行相同。

为什么 libstdc++(这个结果)和 libc++(32/48 字节)都为复制分配分配比复制构造更多的内存?在这两种情况下,新的大小都是已知的。我看不出其中一个在未来更有可能需要额外的内存。

【问题讨论】:

  • 虚拟内存很便宜(大多数情况下是免费的)。在有人使用该内存之前,分配内存不会花费您任何费用。例如,Linux 在有人写入之前不会支持任何物理内存分配。因此,如果您只写入 1MB,则分配 128GB 不会产生任何成本。
  • 解决您的问题。
  • @JesperJuhl:当然,但这是不同的规模。如果每个字符串花费您 41 而不是 26 个字节,那么该空间将被提交,因为它比页面小得多。在我的情况下(内存数据库),这最终在 1 GB 的数据集上浪费了 300 MB。
  • @Yakk-AdamNevraumont - 你能详细说明一下吗?

标签: c++ string stl


【解决方案1】:

我在 libstdc++ 的 basic_string 实现中找到了它。 operator=(const basic_string&amp;) 调用this-&gt;assign,后者调用_M_assign,后者调用_M_create,具有新的(最小)和旧容量。在这里,原始容量增加了一倍。包含指数增长政策的链接作为评论。

首先,我不明白这与我的示例有何关系。毕竟,我只是想替换一个值。

operator=(const basic_string&amp;) 的增长策略在您考虑以下代码而不是单个分配时更有意义:

std::string s;
for (auto i = 100; i < 110; ++i) s = s + std::to_string(i);

在这里,将容量设置为“恰到好处”会违反线性增长要求。

为了解决内存消耗问题,我现在在目标字符串上调用shrink_to_fit()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-11
    • 1970-01-01
    相关资源
    最近更新 更多