【问题标题】:std::string::append crashes program with "std::bad_alloc"std::string::append 使用“std::bad_alloc”使程序崩溃
【发布时间】:2021-10-02 22:11:36
【问题描述】:

我有一个文本文件,其中包含与姓名、位置和高度相关的数据列表。我的程序将这些数据解析为矢量图,然后使用这些数据使用boost::property_tree 构造一个xml 文件。文本文件大约有 3500 行,程序在第 1773 行始终崩溃:

terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Aborted (core dumped)

起初我以为可能达到了大小限制,但阅读std::string 表明目标计算机应该能够分配所需的大小。无论如何,我决定用std::string::sizestd::string::lengthstd::string::capacitystd::string::max_size 进行测试,分别显示:

...
...
6572094845  6572094845 6626476032 9223372036854775807
6579537815  6579537815 6626476032 9223372036854775807
6586984998  6586984998 6626476032 9223372036854775807
6594436394  6594436394 6626476032 9223372036854775807
6601892003  6601892003 6626476032 9223372036854775807
6609351825  6609351825 6626476032 9223372036854775807
6616815856  6616815856 6626476032 9223372036854775807
6624284100  6624284100 6626476032 9223372036854775807

std::string::capacity 增加了一次 std::string::length == std::string::capacity

gdb bt 编译后调试:

(gdb) bt
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1  0x00007fd67037e921 in __GI_abort () at abort.c:79
#2  0x00007fd6709d3957 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007fd6709d9ae6 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4  0x00007fd6709d9b21 in std::terminate() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5  0x00007fd6709d9d54 in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#6  0x00007fd6709da2dc in operator new(unsigned long) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#7  0x00007fd670a6bb8b in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_mutate(unsigned long, unsigned long, char const*, unsigned long) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#8  0x00007fd670a6d133 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_append(char const*, unsigned long) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#9  0x000056104c176f3a in main (argc=1, argv=0x7ffc0af8b9a8) at /home/code/hello_world/createWorld.cpp:224

正在读取的文本文件中的示例行:

713.258 235.418 ABCD1234567     2898

代码:

int main(int argc, char **argv)
{
    CreateWorld *newWorld = new CreateWorld();
    lastModelsParser *lastModels = new lastModelsParser();
    
    /*
      Code here reads creates ifs for xml data, 
      then reads xml successfully into a ptree
    */

    vector<lastModelsParser::lastModel> _lastModels;

    _lastModels = lastModels->getlastModels();

    uint16_t lastModelsEntry = 0;
    std::string newModelString;

    for(auto i:_lastModels){
        ptNewModel = newWorld->modelModifier(ptModel, 
            _lastModels.at(lastModelsEntry).pX,
            _lastModels.at(lastModelsEntry).pY,
            _lastModels.at(lastModelsEntry).name, 
            _lastModels.at(lastModelsEntry).height);

        boost::property_tree::xml_parser::write_xml_element(modelOSS, ptNewModel.front().first, ptNewModel.back().second, 1, xml_settings);
        
        newModelString.append(modelOSS.str());              // CRASHES HERE 
 
        lastModelsEntry++;        
    }

    // append to world.xml
    boost::property_tree::write_xml(worldOSS, ptWorld, xml_settings);           // write xml data into OSStreams
    boost::property_tree::write_xml(modelOSS, ptModel, xml_settings);           // write xml data into OSStreams   
    size_t worldPos = worldOSS.str().find("</world>");

    std::string newWorldString = worldOSS.str().insert(worldPos,newModelString+"\n\t");

    newWorldFile << newWorldString ;
    

    delete(lastModels);
    delete(newWorld);

    return EXIT_SUCCESS;
}                                                                                                                                                                                                                           

编辑。 Valgrind 输出

  1. valgrind --tool=massif --massif-out-file=memleak.txt ./createNewWorld
heap_tree=detailed
n2: 6636657886 (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
 n2: 6626476282 0x5160B89: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_mutate(unsigned long, unsigned long, char const*, unsigned long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)
  n2: 6626476282 0x5162131: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_append(char const*, unsigned long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)
   n0: 6626476033 0x149F38: main (in /home/code/hello_world/createNewWorld)
   n0: 249 in 2 places, all below massif's threshold (1.00%)
  n0: 0 in 2 places, all below massif's threshold (1.00%)
 n0: 10181604 in 18 places, all below massif's threshold (1.00%)
  1. valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --log-file=valgrind-out_1.txt ./createNewWorld
...
--4758-- memcheck GC: 1000 nodes, 0 survivors (0.0%)
--4758-- memcheck GC: 1000 nodes, 0 survivors (0.0%)
--4758-- memcheck GC: 1000 nodes, 0 survivors (0.0%)
--4758-- memcheck GC: 1000 nodes, 0 survivors (0.0%)
==4758== Warning: set address range perms: large range [0xee015040, 0x1b37d5041) (undefined)

【问题讨论】:

  • std::bad_alloc 显示内存快用完了,字符串太大了,输入文件大小是多少?
  • 如果 bad_alloc 由于 RAM 耗尽而没有被抛出,那么很可能是因为您的堆已损坏,导致未来分配失败。我建议在 valgrind(或类似的)下运行你的程序,看看它是否指出了任何非法的内存写入。
  • @prehistoricpenguin 输入文件的大小(以字节为单位)为 436192,包含 3382 行。 std::string::size std::string::max_size 时内存如何耗尽?
  • std::string::max_size 是一个完美的世界价值。如果某处出现问题并耗尽或分散您的存储空间,它可能会超过您可用的存储空间。
  • @JeremyFriesner 谢谢,我已经使用 valgrind 及其工具地块运行。输出太长,无法粘贴到这里,所以我编辑了 OP。

标签: c++ string c++11


【解决方案1】:

std::string::max_size 不是系统可以分配的最大字符串。它只是std::string 类本身可以表示的最大字符串,给定无限的连续内存空间。

您已经超过了 6GB,很有可能根本没有足够的内存来执行下一次重新分配所需的复制。您是否拥有该步骤所需的 13GB RAM,并且您配置的内核限制是否允许为单个进程分配(甚至还没有承诺!)这么多?

只需避免将所有内容存储在单个长字符串中,而是更早地对其进行分区/写出。然后重新分配只会导致内存使用量激增至最大分配量的 2 倍,而不是当前总内存消耗量的 2 倍。

【讨论】:

  • hmmm,看起来我可能不得不重构我的程序,而不是附加一个巨大的 xml 字符串,而是循环它以创建字符串并将其添加到所需的 xml,以便每次迭代完成一次。对于只需要非常小且运行一次的东西,它会占用大量内存。
【解决方案2】:

此代码中崩溃的原因是由于以下行在每次迭代中都会自动运行:

boost::property_tree::xml_parser::write_xml_element(modelOSS, ptNewModel.front().first, ptNewModel.back().second, 1, xml_settings);

newModelString.append(modelOSS.str());  

所以解释一下:

  1. 在每次迭代中,write_xml_element 成员函数都会写入 modelOSS,然后将其附加到 newModelString。
  2. modelOSS 没有被“覆盖”,而是默认附加到。
  3. 这个附加的流随后被附加到 newModelString 上。

基于此,您可以完全删除newModelString.append(modelOSS.str()); 行,它会正常运行。

例如,这就是发生的事情:

New entry = "A"
it = 0, modelOSS = "A", newModelString = "A"
New entry = "B"
it = 1, modelOSS = "A, B", newModelString = "A, A, B"
new entry = "C"
it = 2, modelOSS = "A, B, C", newModelString = "A, A, B, A, A, B, C"

【讨论】:

    【解决方案3】:
    #1  0x00007f64df15bba8 in abort () from /lib64/libc.so.6
    #2  0x00007f64ddc36e20 in ?? () from /lib64/libstdc++.so.6
    #3  0x00007f64ddc44f26 in ?? () from /lib64/libstdc++.so.6
    #4  0x00007f64ddc44fe1 in std::terminate() () from /lib64/libstdc++.so.6
    #5  0x00007f64ddc45378 in __cxa_throw () from /lib64/libstdc++.so.6
    #6  0x00007f64ddc36a7d in ?? () from /lib64/libstdc++.so.6
    #7  0x00007f64ddcfbe1d in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_mutate(unsigned long, unsigned long, char const*, unsigned long) ()
       from /lib64/libstdc++.so.6
    #8  0x00007f64ddcfe51b in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_append(char const*, unsigned long) () from /lib64/libstdc++.so.6
    #9  0x00007f63f4627d2a in encoder::xxx::xxx(std::map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, encoder::TileInfo, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, encoder::TileInfo> > > const&, encoder::TileInfo const&, encoder::CrossTileChapters&, encoder::AddDataInfo const&) ()
    

    【讨论】:

    • 请在您的回答中提供更多详细信息。正如目前所写的那样,很难理解您的解决方案。
    【解决方案4】:

    bool 编码器::xxx::xxx(const std::map<:string tileinfo> &mapTileInfos, const TileInfo &tileInfo,CrossTileChapters &languugeTileChapters,const AddDataInfo &addInfo) { ... std::string 语言 = tileInfo.tileType; std::string addPoiName = ""; 自动 langCode2name = addInfo.langCode2name; if (langCode2name.find(language) != langCode2name.end()) { addPoiName = langCode2name[语言]; } std::string 标签 = languangeTileChapters.labelChapter->GetLablesString(); ... unsigned int langungeTileLabelCount = langungeTileChapters.labelChapter->GetLabelCount(); unsigned int nativeLabelCount = poiTileChapters.labelChapter->GetNativeLabelCount(); 整数偏移量 = -1; bool isFounded = false; offset = GetPOIOffset(poiTileChapters.pointChapter, addInfo.poiSiteID, isFounded, true); 如果(成立){ label.insert(labels.end(), nativeLabelCount - languageTileLabelCount - 1, '\0'); } 别的 { label.insert(labels.end(), nativeLabelCount - languugeTileLabelCount, '\0'); } 标签 += addPoiName; 标签 += '\0'; langugeTileChapters.labelChapter->SetLablesString(labels); 返回真; }

    【讨论】:

    • 请在您的回答中提供更多详细信息。正如目前所写的那样,很难理解您的解决方案。
    猜你喜欢
    • 2019-11-06
    • 2018-12-26
    • 2013-11-19
    • 2010-09-15
    • 2014-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多