【问题标题】:How to release the unused capacity of a string如何释放字符串的未使用容量
【发布时间】:2010-10-18 21:49:21
【问题描述】:

我在我的程序中处理了很多字符串。 这些字符串数据在读入我的程序后的整个生命周期都不会改变。

但是由于 C++ 字符串保留了容量,它们浪费了很多肯定不会被使用的空间。 我试图释放这些空间,但没有成功。

以下是我试过的简单代码:

string temp = "1234567890123456";
string str;

cout << str.capacity() << endl;   

str.reserve(16);    
cout << str.capacity() << endl;     
// capacity is 31 on my computer    

str += temp;    
cout << str.capacity() << endl;    

str.reserve(16);    
cout << str.capacity() << endl;     
// can't release. The capacity is still 31.

(编译器为Visual C++)

我怎样才能释放它?

【问题讨论】:

  • 你的内存真的用完了吗?如果不是,这是过早的优化。如果您的内存已用完,那么哪些对象实际上支配了您的内存分配?关键是这种事情没有帮助。
  • 没有办法强制分配器使用更少的字节用于给定字符串的初始构造 -- 至少在不编写您自己的 STL 分配器类模板(和那时可能还没有)。

标签: c++ string visual-c++ memory-management


【解决方案1】:

尝试使用std::string 交换技巧来缩小字符串:

std::string( str.data(), str.size() ).swap( str ) 

其中str 是您要缩减大小的字符串。

【讨论】:

    【解决方案2】:

    dinkumware STL 的容量永远不会小于 15。 std::basic_string 有一个联合,它是指向已分配缓冲区或 16 字节缓冲区的指针(如果 capacity()

    查看 xstring 头文件

    在您给出的示例中,您保留 16,您实际上是在保留 17(一个用于 null)> 16,因此被分配而不是缓存在缓存指针联合中的 16 字节中。该分配使之前的大小 (16) 翻倍,因此您得到 32。该字符串的容量可能是 31。

    但这取决于 STL 实现。

    在 std::basic_string 的模板 decl 中更改分配器模板参数是不够的 - 何时分配和分配多少的选择在 std::basic_string 增长算法中而不在分配器中。以前大小的两倍(当

    不确定 dinkumware 中的缩小算法......

    【讨论】:

      【解决方案3】:

      这主要是特定于实现的。这个想法是尽量减少分配请求内存碎片。很容易证明,通过每次扩展块时将现有大小加倍,分配计数和内存碎片都被最小化。因此,通常 STL 容器实现会在扩展时将现有块加倍。

      您可以做的一件事是使用自定义分配器,该分配器不会分配超出必要的内存,然后,当您不再需要操作它们时构造您的 std::string 对象(或者在完成操作后,只需换成新的 std ::sting 对象 - 这基本上是其他人在他们的答案中所做的),最后,您可以使用池内存分配器来最小化内存碎片、浪费的 slack 并提高性能。

      见:

      http://www.codeguru.com/cpp/cpp/cpp_mfc/stl/article.php/c4079 http://www.sjbrown.co.uk/2004/05/01/pooled-allocators-for-the-stl/ http://www.codeproject.com/KB/stl/blockallocator.aspx

      搜索“STL 分配器”和“内存池”

      【讨论】:

        【解决方案4】:

        std::string 没有保证的最小容量。您可以通过调用reserve 来请求您想要的任何容量,但特定的实现只能保证将容量设置为大于或等于请求大小的某个量。

        这是你的程序的修改版本,它测试了几种字符串收缩的方法:

        #include <string>
        #include <iostream>
        using namespace ::std;
        
        template< typename S >
        S & reserve_request( S & s, typename S::size_type n ) {
            s.reserve( n ); return s;
        }
        
        template< typename S >
        S & shrink_request1( S & s ) { s.reserve(); return s; }
        
        template< typename S >
        S & shrink_request2( S & s ) { S( s ).swap( s ); return s; }
        
        template< typename S >
        S & shrink_request3( S & s ) { S( s.c_str() ).swap( s ); return s; }
        
        template< typename S >
        void test( S & s ) { cout << s.capacity() << endl; }
        
        int main() {
            string temp = "1234567890123456";    // length 16
            string str;
        
            test( str );                         // 15
            test( reserve_request( str, 16 ) );  // 31
            test( str += temp );                 // 31
            test( reserve_request( str, 16 ) );  // 31
            test( shrink_request1( str ) );      // 31
            test( shrink_request2( str ) );      // 31
            test( shrink_request3( str ) );      // 31
            return 0;
        }
        

        看起来 Visual C++ 的 std::string 通常会保留一些备用容量。

        如果您的项目加载了从外部源读取的大量字符串,这些字符串的大小永远不会改变,您最好(正如其他人建议的那样)将它们存储在由'\0' 字符分隔的单个大块字符内存中(即,作为 C 字符串)。如果您愿意,您可以提供即时返回 std::strings 的包装函数。

        【讨论】:

          【解决方案5】:

          拼写Naveen's answer:

          string x = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
          cerr << x.capacity() << "\n";    // MSVC++: 63    g++: 52
          
          // This tends not to work (although in theory it could):
          //x = "XYZ";
          //cerr << x.capacity() << "\n";  // MSVC++: 63    g++: 52
          
          // This tends to work (although in theory it might not):
          string("XYZ").swap(x);
          cerr << x.capacity() << "\n";    // MSVC++: 15    g++: 3
          

          请注意,如果底层分配器在构造长度为 nstring 时分配的字节数超过 n 个(例如,通过四舍五入到最接近的 32,因为 MSVC++ 似乎做),没有办法让它使用更少的字节。但是您可能无论如何都不想这样做,因为这种“四舍五入”是为了使动态内存分配过程更有效,并且还具有平均更快地连接短字符串的副作用(因为更少的重新分配需要发生)。

          【讨论】:

            【解决方案6】:

            我认为您可以使用交换方法来释放数据。用一个空的本地字符串交换它,这样当本地字符串超出范围时,内存就会被释放。

            【讨论】:

            • +1。如果要将字符串设置为其他内容,这是一种返回内存的好方法(尽管不能保证)。尽管如果底层分配器将其四舍五入到最接近的 16 个字节(正如 MSVC++ 似乎所做的那样),这无助于修剪空间,但没有任何帮助。
            【解决方案7】:

            当您致电reserve 时,您正在提出请求 以更改容量。实现将仅保证保留等于或大于此数量的数字。 因此,shrink 容量的请求可能会被特定实现安全地忽略。

            但是,我建议您考虑这是否不是过早的优化。你确定你真的制作了这么多的字符串,这对你来说是一个内存瓶颈吗?您确定瓶颈实际上是内存吗?

            来自reserve的文档:

            这可以扩大或缩小 字符串中的存储空间, 虽然注意到结果 调用此函数后的容量 不一定等于res_arg 但可以等于或大于 比res_arg,因此缩小 请求可能会也可能不会产生 分配的实际减少 特定库中的空间 执行。无论如何,它永远不会 修剪字符串内容(为此 目的,请参阅调整大小或清除,其中 修改内容)。

            【讨论】:

              【解决方案8】:

              那你为什么不使用 char 数组呢?

              【讨论】:

              • +1。当您希望使用它并在以后销毁时,可以将库的 char 数组转换为 std::string。
              • 假设您的意思是动态分配,即使这样也可能“浪费”至少 4 个字节来保存长度,并且内部动态分配器可能会分配不小于 16/32 字节的块出于效率原因。
              猜你喜欢
              • 2021-06-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-11-15
              相关资源
              最近更新 更多