Как исчерпывается память в куче? - PullRequest
6 голосов
/ 13 октября 2019

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

Я тестирую две разные программы. Первая программа создает векторные объекты в куче. Вторая программа создает целочисленные объекты в куче.

Вот мой код:

#include <vector>
#include <stdio.h>

int main()
{
    long long unsigned bytes = 0;
    unsigned megabytes = 0;

    for (long long unsigned i = 0; ; i++) {

        std::vector<int>* pt1 = new std::vector<int>(100000,10);

        bytes += sizeof(*pt1);
        bytes += pt1->size() * sizeof(pt1->at(0));
        megabytes = bytes / 1000000;

        if (i >= 1000 && i % 1000 == 0) {
            printf("There are %d megabytes on the heap\n", megabytes);
        }

    }
}

Окончательный вывод этого кода до получения ошибки bad_alloc: «В куче 2000 мегабайт»

Во второй программе:

#include <stdio.h>

int main()
{
        long long unsigned bytes = 0;
        unsigned megabytes = 0;

        for (long long unsigned i = 0; ; i++) {

           int* pt1 = new int(10);

           bytes += sizeof(*pt1);
           megabytes = bytes / 1000000;

           if (i >= 100000 && i % 100000 == 0) {
              printf("There are %d megabytes on the heap\n", megabytes);
        }

    }
}

Окончательный вывод этого кода до получения ошибки bad_alloc: «В куче 511 мегабайт»

Окончательный вывод в обеих программахсильно отличаетсяЯ что-то неправильно понимаю в бесплатном магазине? Я думал, что оба результата будут примерно одинаковыми.

Ответы [ 2 ]

3 голосов
/ 13 октября 2019

Весьма вероятно, что указатели, возвращаемые new на вашей платформе, выровнены по 16 байтов .

Если int равно 4 байтов, это означает, что для каждогоnew int(10) вы получаете четыре байта и лишаете возможности использовать 12 байтов.

Уже одно это объясняет разницу между получением 500 МБ полезного пространства из небольших ресурсов и 2000 МБ из больших.

Наверхиз этого накладные расходы на отслеживание выделенных блоков (как минимум, их размера и того, свободны они или используются). Это очень специфично для распределителя памяти вашей системы, но также связано с накладными расходами. См. «Что такое чанк» в https://sourceware.org/glibc/wiki/MallocInternals для объяснения распределителя glibc.

0 голосов
/ 13 октября 2019

Прежде всего вы должны понимать, что операционная система выделяет память для обработки в довольно больших кусках памяти, называемых страницами (это аппаратное свойство). Размер страницы составляет около 4 -16 кБ.

Теперь стандартная библиотека пытается эффективно использовать память. Так что нужно найти способ нарезать страницы на мелкие кусочки и управлять ими. Для этого нужно сохранить некоторую дополнительную информацию о структуре кучи.

Вот круто Андрей Александреску cppcon говорит более или менее о том, как это работает (без информации об управлении страницами).

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

Обратите также внимание, что иногда в зависимости от стратегии кучи (когда запрашивается небольшой объем памяти) она большеэффективно тратить немного памяти и возвращать больший объем памяти, чем было запрошено.

...