views:

97

answers:

2

I have a piece of C++ code that deallocates memory as follows

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

While the code works ok, when called from an exception handler, it runs very slowly. Looking at task manager while single stepping through the above loop, the amount of physical memory used by the process (Mem Usage) increases with each call to free, whereas the virtual memory (VM Size) stays the same. Eventually the process terminates abnormally.

The code that allocates the memory that throws the exception is as follows;

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

Now at a glance I suspect that if I reverse the ordering of the deallocation loop, such that the last block allocated is the first freed, I might avoid this problem. But is there any reason why process memory usage should increase with a call to free, and why calling free on a valid heap memory block should cause the program to terminate? (n.b. the termination could well be debugger related rather than the app itself).

Edit: The OS is Windows XP SP3, the compiler is MSVC++ 8

Edit2: A more complete version of the code is as follows;

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);
        }
    }
}
+3  A: 

Your allocation loop counts from Pages up to NewPages while your deallocation loop counts from Pages-1 down to NewPages. Only one can be correct, depending if Pages <= NewPages or not.

If the deallocation loop is the one that is wrong, than you are trying to free "pointers" that are just random memory values, possibly causing anything, like abnormal termination of your program.

Aside of that, calling free() won't necessarily decrease the memory size of your program. It depends on the implementation of malloc()/free() how exactly this memory is handled internally. malloc will manage the memory internally in some way and doesn't necessarily immediately return anything freed to the OS. It probably just internally marks it as "free" and would reuse it on later calls to malloc(). Also if there are other still allocated chunks of memory on the same memory page it can't give the page back to the OS yet.

The physical memory used might increase because the memory that is getting freed needs to be swapped in, if your malloc implementation stores some management information next to the allocated block (for example how big the block is). Free will need to read/modify this management information to do its work, and for that the memory needs to be swapped in first.

sth
A bit of explanation on the variables, Pages is the current number of pages in the array, NewPages is the requested number of pages, which can be smaller to call the first piece of code to decrease the array sixe, or greater to call the second piece of code to increase the size of array. I've edited the question to better reflect what is happening here.
Shane MacLaughlin
+1  A: 

Your process can use two megabytes of memory. When malloc fails, the used memory will be near that value. If your computer has less physical memory, a lot of memory pages of that process will have been paged out to the disk.

While your exception handler frees back memory, the heap manager will touch all pages, bringing them back to physical memory. This will be slow.

This is what you are observing with the Task Manager. The column names are deceptives: VM Size is really private memory, which will not change because freed memory is not given back to the system by the Heap Manager (answering why would be a question by itself). Mem Usage is the size of the working set of the process, that is the physical memory used by that process. It will increase as pages are mapped back to physical memory from the page file.

You do not mention how the process terminates abnormally, but fragmentation may also occur in memory, which may explain that mallocs may continue to fail even after freeing your array.

plodoc