物理内存量随着空闲块的增加而增加

时间:2010-10-05 11:38:34

标签: c++ memory-management

我有一段C ++代码,可以按如下方式释放内存

for (int i = Pages-1; i >= NewPages; i--)
{
    LPVOID p = m_Pages[i];
    free(p);
}

虽然代码工作正常,但是当从异常处理程序调用时,它运行得非常慢。在单步执行上述循环时查看任务管理器,进程使用的物理内存量(Mem Usage)随着每次调用free而增加,而虚拟内存(VM Size)保持不变。最终,该过程异常终止。

分配抛出异常的内存的代码如下:

for (int i = Pages; i < NewPages; i++)
{
    LPVOID p = malloc(m_ElementsPerPage*m_ElementSize);
    if (!p)
        AfxThrowMemoryException( );
    m_Pages.Add(p);
}

现在一眼我怀疑如果我反转释放循环的顺序,这样分配的最后一个块是第一个被释放的,我可能会避免这个问题。但是有没有理由为什么进程内存使用会随着对free的调用而增加,为什么在有效的堆内存块上调用free会导致程序终止? (n.b.终止可能与调试器有关,而不是应用程序本身)。

编辑:操作系统是Windows XP SP3,编译器是MSVC ++ 8

编辑2:更完整的代码版本如下:

void  MyArray::ResizeArray(int NewPages)
{
    int Pages = m_Pages.GetSize();
    if (NewPages != Pages)
    {
        if (NewPages > Pages)   // Grow the page array
        {
            for (int i = Pages; i < NewPages; i++)
            {
                LPVOID p = malloc(m_ElementsPerPage*m_ElementSize);
                if (!p)
                    AfxThrowMemoryException( );
                m_Pages.Add(p);
            }
        } else  // Shrink the page array
        {
            for (int i = Pages-1; i >= NewPages; i--)
            {
                LPVOID p = m_Pages[i];
                    free(p);
            }
            m_Pages.SetSize(NewPages);
        }
    }
}

2 个答案:

答案 0 :(得分:3)

当您的解除分配循环从Pages减少到NewPages时,您的分配循环从Pages-1计算到NewPages。只有一个可以是正确的,具体取决于Pages <= NewPages

如果解除分配循环是错误的,那么你试图释放只是随机内存值的“指针”,可能会导致任何问题,例如异常终止程序。

除此之外,调用free()不一定会减少程序的内存大小。这取决于malloc() / free()的实现如何在内部处理这个内存。 malloc将以某种方式在内部管理内存,并不一定立即返回释放到操作系统的任何内容。它可能只是在内部将其标记为“免费”,并将在以后调用malloc()时重复使用它。此外,如果在同一个内存页面上还有其他仍然分配的内存块,则无法将页面返回给操作系统。

如果您的malloc实现在分配的块旁边存储一些管理信息(例如块的大小),则使用的物理内存可能会增加,因为需要交换释放的内存。 Free需要读取/修改此管理信息才能完成其工作,为此需要首先交换内存。

答案 1 :(得分:1)

您的进程可以使用2兆字节的内存。当malloc失败时,使用的内存将接近该值。如果您的计算机具有较少的物理内存,则该进程的大量内存页面将被分页到磁盘。

当您的异常处理程序释放内存时,堆管理器将触及所有页面,将它们带回物理内存。这将很慢。

这是您使用任务管理器观察的内容。列名是欺骗性的:VM Size实际上是私有内存,它不会改变,因为Heap Manager没有将释放的内存返回给系统(回答为什么它本身就是一个问题)。 Mem Usage是进程工作集的大小,即该进程使用的物理内存。当页面从页面文件映射回物理内存时,它会增加。

您没有提到进程异常终止的方式,但内存中也可能出现碎片,这可能解释了即使释放数组后malloc也可能继续失败。