【问题标题】:Bad allocation error when populating std::vector填充 std::vector 时出现错误分配错误
【发布时间】:2013-10-12 19:17:24
【问题描述】:

我正在将三个大型二进制文件(每个 c.180Mb)读取到 std::vector 中,如下所示:

m_ifStream.open("myfile.dat", std::ios::binary | std::ios::in);

if (m_ifStream)
{
    //Obtain input stream length
    m_ifStream.seekg (0, ios::end);
    streamLength = (size_t)(m_ifStream.tellg());
    m_ifStream.seekg (0, ios::beg);

    //Reserve doesn't work around the problem, may be more efficient though...
    //m_buffer = new vector<unsigned char>();
    //m_buffer->reserve(streamLength);

    //Next line sometimes results in bad_alloc when reading a large file
    m_buffer = new vector<unsigned char>((std::istreambuf_iterator<char>(m_ifStream)), (std::istreambuf_iterator<char>()));
}

填充向量的调用失败,抛出“错误分配”异常。

读取第一个文件时填充有时会失败;在其他时候,它在第二次或第三次失败。我正在使用 Visual Studio 2010 并将我的代码编译为 32 位,它应该能够寻址高达 2Gb。我在一台有 16Gb RAM 的机器上运行,至少有 10Gb 可用,所以可用内存不足不是问题。该错误发生在调试和发布配置中。

使用reserve 预分配内存没有帮助。

向量的 max_size 属性返回 2^32,因此它似乎不是容器中的限制。

代码很好,有大量合并大小 > 180Mb 的小文件,导致我认为我的代码达到了边界。

是否有一种可接受的方式从大型输入文件中填充向量?我想避免遍历文件中的每个字节,并认为使用istreambuf_iterator 会针对这种操作进行优化。

【问题讨论】:

    标签: c++ file-io vector


    【解决方案1】:

    如果您想让reserve() 的调用对实际读数产生任何影响,您应该创建一个临时的std::vector&lt;unsigned char&gt; 并将这个临时分配给目标向量。相反,您可以使用类似

    m_buffer->assign(std::istreambuf_iterator<char>(m_ifStream),
                     std::istreambuf_iterator<char>());
    

    在不保留的情况下读取文件可能会以某种方式分散您的内存,但我不认为程序会耗尽像您这样的小文件的内存(几 GB 的文件可以被认为是大文件;160MB 是'不是很大)。如果您知道文件的大小,则最好使用read() 成员读取文件:

    m_buffer->resize(streamLength);
    m_ifStream.read(reinterpret_cast<char*>(m_buffer->data()), streamLength);
    

    我个人的猜测是,std::bad_alloc 异常实际上是由于确定文件大小的错误造成的。例如,我认为std::size_t 不一定大到足以容纳std::streamsize。此外,没有尝试验证任何这些操作是否成功,如果无法打开流,seekg() 将返回pos_type(-1),这将转换为相当大的std::size_t

    【讨论】:

      【解决方案2】:

      您正在做更多的工作并分配比您需要的更多的内存。

      先去掉指针,什么都不加

      vector<char> m_buffer;
      

      然后调用resize(不是保留)到正确的大小

      m_buffer.resize(streamLength);
      

      如果你要耗尽内存,这就是它会发生的时候。

      最后将数据直接读入vector,不要使用streambuf_iterator,谁知道幕后是什么

      m_ifStream.read(&m_buffer[0], streamLength);
      

      这里的主要好处是你只分配了一个向量(你的代码有两个向量,其中一个被复制到另一个之上),其次你已经删除了所有的绒毛,只留下了两个基本操作,分配内存,读取文件。

      【讨论】:

      • 通常应该调整大小而不是保留?
      • @pdm2011 不,因为他们做不同的事情。 reserve 分配内存但不调整向量的大小,resize 确实调整了向量的大小(这显然意味着分配内存)。在您的代码中,向量正在通过以下操作隐式调整大小,在我的代码中,我需要显式调整向量的大小,因为我直接读取它,所以在我这样做之前它必须是正确的大小。
      • 感谢您的回复。我使用了你的建议,但不幸的是,同样的问题发生了,这次是第四次调整大小。我的怀疑是代码中其他地方的堆损坏,但我没有发现任何有罪的东西,并且代码对于大量较小的文件是健壮的。我正在使用生成的包装器在 C# 和非托管代码之间编组,这可能会进行一些过多的容器复制 - 我将尝试通过分析器运行它,并查看 64 位构建有什么影响。
      【解决方案3】:
      m_buffer = new vector<unsigned char>();
      m_buffer->reserve(streamLength);
      
      //Next line sometimes results in bad_alloc when reading a large file
      *m_buffer = vector<unsigned char>((std::istreambuf_iterator<char>(m_ifStream)), (std::istreambuf_iterator<char>()));
      

      让我印象深刻的第一件事是您正在覆盖 vector 的一个已经预先分配的区域。如果您随后创建一个新向量来覆盖该向量,那么执行“保留”显然没有意义。这只是意味着您必须为这两个相对较大的向量留出空间。

      我将首先将m_buffer 更改为不是指向向量的指针-这样,您不必调用new vector&lt;unsigned char&gt;-拥有指向向量的指针几乎没有用处[充其量,您如果您的向量不包含任何内容,请节省大约 16 个字节]。

      然后删除reserve。看看情况如何。

      【讨论】:

      • 这实际上是我最初所做的 - 我介绍了储备,因为我读过它可能会有所帮助。我已经编辑了问题以反映这一点。
      猜你喜欢
      • 2014-11-25
      • 1970-01-01
      • 1970-01-01
      • 2022-12-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-26
      相关资源
      最近更新 更多