Объем физической памяти увеличивается при освобождении блоков - PullRequest
1 голос
/ 05 октября 2010

У меня есть фрагмент кода C ++, который освобождает память следующим образом

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

Хотя код работает нормально, при вызове из обработчика исключений он работает очень медленно. Если посмотреть на диспетчер задач при пошаговом выполнении вышеуказанного цикла, объем физической памяти, используемой процессом (использование памяти), увеличивается с каждым вызовом освобождения, тогда как виртуальная память (размер виртуальной машины) остается неизменной. В конце концов процесс завершается ненормально.

Код, выделяющий память, которая выдает исключение, выглядит следующим образом:

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

Теперь с первого взгляда я подозреваю, что если я переверну порядок цикла освобождения, чтобы последний выделенный блок был первым освобожденным, я мог бы избежать этой проблемы. Но есть ли какая-то причина, по которой использование памяти процесса должно увеличиваться при вызове free, и почему вызов free для допустимого блока памяти кучи должен привести к завершению программы? (нет, окончание вполне может быть связано с отладчиком, а не с самим приложением).

Редактировать: Операционная система - Windows XP SP3, компилятор - MSVC ++ 8

Edit2: Более полная версия кода выглядит следующим образом:

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 ]

3 голосов
/ 05 октября 2010

Ваш цикл распределения считается от Pages до NewPages, а цикл освобождения - от Pages-1 до NewPages. Только один может быть правильным, в зависимости от того, Pages <= NewPages или нет.

Если петля освобождения является ошибочной, то вы пытаетесь освободить «указатели», которые являются просто случайными значениями памяти, что может вызвать что-либо, например, аварийное завершение вашей программы.

Кроме того, вызов free() не обязательно уменьшит объем памяти вашей программы. От реализации malloc() / free() зависит, как именно эта память обрабатывается внутри. malloc каким-то образом управляет памятью и не обязательно сразу возвращает что-либо освобожденное в ОС. Возможно, он просто помечает его как «свободный» и будет использовать его при последующих вызовах malloc(). Также, если на той же странице памяти есть другие выделенные фрагменты памяти, он не может вернуть страницу в ОС.

Используемая физическая память может увеличиться, поскольку освобождаемая память должна быть заменена, если ваша реализация malloc хранит некоторую информацию управления рядом с выделенным блоком (например, насколько велик блок). Свободному потребуется прочитать / изменить эту управляющую информацию, чтобы выполнить свою работу, и для этого сначала необходимо заменить память.

1 голос
/ 05 октября 2010

Ваш процесс может использовать два мегабайта памяти. При сбое malloc используемая память будет близка к этому значению. Если на вашем компьютере меньше физической памяти, многие страницы этого процесса будут выгружены на диск.

Пока ваш обработчик исключений освобождает память, менеджер кучи будет касаться всех страниц, возвращая их в физическую память. Это будет медленно.

Это то, что вы наблюдаете с помощью диспетчера задач. Имена столбцов вводят в заблуждение. Размер виртуальной машины - это действительно частная память, которая не изменится, поскольку освобожденная память не возвращается системе диспетчером кучи (отвечая на вопрос, почему это вопрос сам по себе). Mem Usage - это размер рабочего набора процесса, то есть физической памяти, используемой этим процессом. Он будет увеличиваться при отображении страниц обратно в физическую память из файла подкачки.

Вы не упоминаете, как процесс ненормально завершается, но в памяти также может происходить фрагментация, что может объяснить, что mallocs могут продолжать отказывать даже после освобождения массива.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...