我有一段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);
}
}
}
答案 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也可能继续失败。