计算 std::string 的分配内存(以及 std::vector 中字符串的使用)

Calculate allocated memory of std::string (and the use of strings in std::vector)

我想计算在创建字符串并为其赋值时分配了多少内存。

string s = "";
cout << sizeof(s) << endl;
cout << sizeof(s.at(0)) * s.capacity() << endl;
s = "1234567890qwertz";
cout << sizeof(s) << endl;
cout << sizeof(s.at(0)) * s.capacity() << endl;

这是我的字符串消耗的所有内存吗?我通过简单地调用 sizeof(s) (在我的机器上是 40 个字节)得到的 initial/static 部分 plus 动态部分 - 一个字符的大小乘以分配的占位符使字符串可以有效地调整大小(在我的机器上,字符串 s 首先分配一个 15 字节的块,直到文本太长,所以在第二次分配之后,动态部分为 31 字节)。为什么不是 16 和 32 字节呢?

这种思路(static + dynamic for each string is all the memory it occurs)是否正确?

意味着如果我有一个 std::vector 字符串,并且我想计算该向量的所有内存,我需要做同样的事情:我添加 initial/static 我的向量的大小加上动态部分,这意味着一个字符串占用的总内存,就像我上面对向量中的每个字符串所做的那样?

vector<string> cache;
// fill cache with strings of dynamic length
int size = sizeof(cache);
for (int i = 0; i < cache.size(); i++)
{
    size += sizeof(cache[i]);
    size += sizeof(cache[i].at(0)) * cache[i].capacity();
}

总而言之,我的 "cache" 占用的内存量是否正确?

编辑: 或者我还需要考虑 std::vector 本身也有 .capacity() >= .size() 这可能意味着我实际上需要这样做:

对于每个 cache.capacity() - 我需要添加 sizeof(cache[i]) 并且另外 对于每个 cache.size() - 添加 sizeof(cache[i].at(0)) * cache[i].capacity() ??

这个问题很难回答。天真地你会认为消耗的内存总量是

vector_capacity * sizeof(std::string) + sum_of_capacity_of_each_string_in_the_vector

但这更多的是一个上限,并不是真正能够消耗的。例如,short string optimization 允许 std::string 将字符串数据存储在字符串对象本身消耗的存储空间中(您称之为静态大小)。如果是这样,那么实际消耗的 space 将是

vector_capacity * sizeof(std::string)

向量中每个字符串的容量就是您在不分配任何额外 space 的情况下占用了多少 space。您将需要检查您的实现以查看它是否使用 SSO 以及它将存储在字符串对象中的字符串的长度,以实际了解容量值是使用内部字符串 space 还是实际消耗额外的内存。这使得实际 space 消耗

vector_capacity * sizeof(std::string) + sum_of_capacity_of_each_string_in_the_vector_where_
                                        the_capcity_is_more_than_the_sso_amount

在你的计算中sizeof(cache[i].at(0))是不需要的。 std::string 使用 char 并且 sizeof(char) 保证是 1

字符串的容量比你想象的少一个,原因很简单,那就是

s.c_str()

一个 C++ 字符串存储在一个内存块中,其容量给出了总大小和使用的大小 space。但是 C 字符串以 0 终止。 C++ 字符串在内存块的末尾保留一个额外的字节来存储 0。这样 s.c_str() 总是以 0 终止。

所以字符串动态部分使用的内存是capacity + 1。

关于字符串或字符串向量消耗的总内存,NathanOliver 回答说我认为。但要注意多次持有相同字符串的向量。

如果您想知道您的 std::vector<std::string> 使用了多少 space,请计算它:

auto netto_memory_use(std::vector<std::string> const& x) noexcept {
    return std::accumulate(
        begin(x),
        end(x),
        sizeof x + sizeof x[0] * x.capacity(),
        [](auto n, auto&& s) {
            if (std::less<void*>()(data(s), &s)
            || std::greater_eq<void*>()(data(s) + s.capacity(), &s + 1))
                return n + s.capacity() + 1;
            return n;
        });
    }

我使用 std::less<void*> / std::greater_eq<void*> 来利用它们定义完整的顺序,而不是仅使用比较运算符。

累加器在添加字符串的容量之前测试应用的小字符串优化 (SSO)。当然,所有容量为 0 的字符串都可以共享同一个静态分配的终止符。或者 capacity and/or length 可以和 character-data 一起分配。
尽管如此,除了内存管理系统开销之外,这应该是所用内存的一个很好的近似值。