Почему мой класс стека использует так много памяти? - PullRequest
2 голосов
/ 18 мая 2019

Я создал собственный стек на основе одного связанного списка, и он работает нормально, но когда я взглянул на использование памяти ... Консольное приложение, использующее стек, содержащий целых 100 000, заняло 6,5 МБ. Это ужасно, потому что 4 байта * 100k = 0,38 МБ. Я выделяю память для каждой структуры 'Unit', которая содержит указатель на следующую, но я не думаю, что это заняло бы много памяти. В чем причина проблемы?

template <typename T>
class Stack
{
        struct Unit
    {
        Unit *prev;
        T value;
        Unit(T value);
    };
public:
    Stack();
    void Push(T value);
    int Count();
    T Top();
    T Pop();
    ~Stack();
private:
    unsigned int count;
    Unit *top;

};

template<typename T>
Stack<T>::Unit::Unit(T value)
{
    this->value = value;
    prev = nullptr;
}

template<typename T>
Stack<T>::Stack()
{
    top = nullptr;
    count = 0;
}

template<typename T>
void Stack<T>::Push(T value)
{
    if (top == nullptr)
    {
        top = new Unit(value);
    }
    else
    {
        Unit *tmp = new Unit(value);
        tmp->prev = top;
        top = tmp;
    }
    count++;
}

template<typename T>
T Stack<T>::Pop()
{
    T value = top->value;
    Unit *tmp = top->prev;
    delete top;
    top = tmp;
    count--;
    return value;
}

template<typename T>
Stack<T>::~Stack()
{
    Unit *curr = top;
    if (!curr)
    {
        return;
    }
    while (curr)
    {
        Unit* tmp = curr->prev;
        delete curr;
        curr = tmp;
    }
}

1 Ответ

3 голосов
/ 18 мая 2019

При расчете размера вы не учитывали размер указателя и любые отступы, которые может иметь структура. Указатель, вероятно, 4 или 8 байт на вашей платформе. Заполнение обсуждается здесь: Заполнение структуры в C ++

Я добавил cout в конструктор для вашего стека, чтобы показать размер вашей структуры Unit:

#include <iostream>
template<typename T>
Stack<T>::Stack()
{
    top = nullptr;
    count = 0;

    std::cout << "The size of each unit is "  << sizeof(Unit) << " bytes." << std::endl;
} 

// Rest of code is copied directly from the question.

int main() {

    Stack<int> myStack;

    return 0;
}

И результат был:

The size of each unit is 16 bytes.

Полный пример здесь: https://ideone.com/TWPvDv

Edit:

Увидев, что человек, задающий вопрос, использует Visual Studio, я провел дополнительную отладку, чтобы понять ситуацию. В режиме отладки среда выполнения отладки добавляет дополнительное пространство к каждому выделению для обеспечения обнаружения повреждения кучи и отслеживания кучи. Я установил точку останова до конца основного и посмотрел на использование памяти в TaskManager (да, не самое точное измерение). В режиме отладки все приложение использовало более 12 МБ, а в версии Release общее использование памяти составило 2,6 МБ.

Информация о дополнительном выделении на блок приведена здесь:

https://docs.microsoft.com/en-us/visualstudio/debugger/crt-debug-heap-details?view=vs-2019

Отладочные версии функций кучи вызывают стандарт или базу версии, используемые в сборках Release. Когда вы запрашиваете блок памяти, менеджер отладочной кучи выделяет из базовой кучи немного больше Блок памяти, чем запрашивается, и возвращает указатель на вашу часть этого блока. Например, предположим, что ваше приложение содержит вызов: malloc (10). В сборке Release malloc будет вызывать базу подпрограмма выделения кучи, запрашивающая выделение 10 байтов. В Отладочная сборка, однако, malloc будет вызывать _malloc_dbg, который затем вызвать подпрограмму выделения основной кучи с запросом выделения 10 байт плюс примерно 36 байт дополнительной памяти. Все результирующие блоки памяти в куче отладки соединены в один связанный список, упорядоченный в соответствии с тем, когда они были распределены.

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