Почему программа VC ++, которая хранит 5 МБ данных, потребляет 64 МБ системной памяти? - PullRequest
5 голосов
/ 11 июня 2011

Я пытался выяснить, почему моя программа потребляет так много системной памяти. Я загружаю файл с диска в вектор структур из нескольких динамически размещаемых массивов. Файл 16 МБ в итоге потребляет 280 МБ системной оперативной памяти в соответствии с диспетчером задач. Типы в файле - это в основном символы с некоторыми шортами и несколькими лонгами. В файле содержится 331 000 записей, содержащих в среднем около 5 полей. Я преобразовал вектор в структуру, и это уменьшило объем памяти до 255 МБ, но это все еще кажется очень высоким. Поскольку вектор занимает так много памяти, программе не хватает памяти, поэтому мне нужно найти способ сделать использование памяти более разумным.

Я написал простую программу, чтобы просто заполнить вектор (или массив) 1 000 000 указателей на символы. Я ожидал бы, что он выделит 4 + 1 байт для каждой 5 МБ памяти, необходимой для хранения, но на самом деле он использует 64 МБ (версия массива) или 67 МБ (векторная версия). Когда программа запускается в первый раз, она потребляет только 400 КБ, так почему же дополнительно выделяется 59 МБ для массива или 62 МБ для векторов? Эта дополнительная память, похоже, предназначена для каждого контейнера, поэтому, если я создаю size_check2, копирую все и запускаю его, программа использует 135 МБ для 10 МБ указателей и данных.

Заранее спасибо,

size_check.h

#pragma once

#include <vector>

class size_check
{
public:
    size_check(void);
    ~size_check(void);

    typedef unsigned long   size_type;

    void stuff_me( unsigned int howMany );

private:
    size_type**         package;
//  std::vector<size_type*> package;
    size_type*          me;
};

size_check.cpp

#include "size_check.h"

size_check::size_check(void)
{
}

size_check::~size_check(void)
{
}

void size_check::stuff_me( unsigned int howMany )
{
    package = new size_type*[howMany];
    for( unsigned int i = 0; i < howMany; ++i )
    {

        size_type *me = new size_type;
        *me = 33;
        package[i] = me;
//      package.push_back( me );
    }
}

main.cpp #include "size_check.h"

int main( int argc, char * argv[ ] )
{
    const unsigned int buckets = 20;
    const unsigned int size = 50000;

    size_check* me[buckets];

    for( unsigned int i = 0; i < buckets; ++i )
    {
        me[i] = new size_check();
        me[i]->stuff_me( size );
    }
    printf( "done.\n" );
}

Ответы [ 4 ]

3 голосов
/ 11 июня 2011

В моем тесте с использованием VS2010 отладочная сборка имела размер рабочего набора 52 500 КБ. Но у сборки выпуска был рабочий набор размер 20,944КБ.

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

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

1 голос
/ 18 июня 2011

Я думаю, что нашел ответ, углубившись в новое утверждение. В отладочных сборках есть два элемента, которые создаются при создании нового. Одним из них является _CrtMemBlockHeader, длина которого составляет 32 байта. Другой - noMansLand (забор памяти) с размером 4 байта, который дает нам служебную информацию в 36 байтов для каждого нового. В моем случае каждый отдельный новый для символа стоил мне 37 байт. В сборках релиза использование памяти сокращено примерно до 1/2, но я не могу точно сказать, сколько выделено для каждого нового, так как я не могу добраться до процедуры new / malloc.

Так что моя работа заключается в том, чтобы выделить большой блок памяти для хранения файла в памяти. Затем анализируют изображение памяти, заполняя вектор указателей на начало каждой записи. Затем по требованию я строю запись из образа памяти, используя указатель на начало выбранной записи. Это уменьшило объем памяти до <25 МБ. </p>

Спасибо за вашу помощь и предложения.

1 голос
/ 11 июня 2011

Может быть несколько причин, почему вы видите такой большой объем памяти в вашей тестовой программе. Внутри вашего

void size_check::stuff_me( unsigned int howMany )
{

Этот метод всегда вызывается с howMany = 50000.

package = new size_type[howMany];

Предполагая, что это на 32-битной установке, вышеприведенный оператор выделит 50 000 * 4 байта.

for( unsigned int i = 0; i < howMany; ++i )
{
    size_type *me = new size_type;

Выше будет выделять новое хранилище на каждой итерации цикла. Так как это зацикливает 50 000, а выделение никогда не удаляется, это фактически занимает еще 50000 * 4 байта после завершения цикла.

        *me = 33;
        package[i] = *me;
    }
}

Наконец, поскольку stuff_me() вызывается 20 раз из main(), ваша программа выделяла бы по крайней мере ~ 8 Мбайт после завершения. Если это в 64-битной системе, то занимаемая площадь, вероятно, удвоится, поскольку sizeof(long) == 8 байтов.

Увеличение потребления памяти может быть связано с тем, как VS реализует динамическое распределение. По соображениям производительности, возможно, что из-за нескольких вызовов new ваша программа резервирует дополнительную память, чтобы избежать загрузки ОС каждый раз, когда ей требуется больше.

К вашему сведению, когда я запускал вашу тестовую программу на mingw-gcc 4.5.2, потребление памяти составляло ~ 20 Мбайт - намного ниже, чем вы видели, но все равно было значительным. Если я изменил метод stuff_me на это:

void size_check::stuff_me( unsigned int howMany )
{
    package = new size_type[howMany];
    size_type *me = new size_type;
    for( unsigned int i = 0; i < howMany; ++i )
    {
        *me = 33;
        package[i] = *me;
    }
    delete me;
}

Потребление памяти несколько снижается до ~ 4-5 Мбайт.

1 голос
/ 11 июня 2011

Утечка памяти

package = new size_type[howMany]; // instantiate 50,000 size_type's
for( unsigned int i = 0; i < howMany; ++i )
{
    size_type *me = new size_type; // Leak: results in an extra 50k size_type's being instantiated
    *me = 33;
    package[i] = *me;  // Set a non-pointer to what is at the address of pointer "me"
    // Would package[i] = 33; not suffice?
}

Кроме того, убедитесь, что вы скомпилировали в режиме выпуска

...