почему память, выделяемая программой, не уменьшается?

0

Существует правило, указывающее, что local variables удаляются после завершения функции вызова.

Я попытался вызвать функцию (в C++)

void DoIt()
{   
    double x[100000];
}

и исследовал, что в момент создания массива x память, выделенная программой, увеличивается на несколько KBytes. Однако после завершения функции вызова память не уменьшается. Также функция delete дает ошибку времени выполнения.

Итак, почему память, выделенная программой, не уменьшается после функции завершения вызова? Есть ли способ удалить локальную переменную, как указано выше?

  • 0
    Как вы измеряете использование памяти?
  • 2
    Ваше измерение может быть ошибочным. x выделяется в стеке. Вы не можете (и не можете с кодом, который вы показали) вызвать вызов на нем.
Показать ещё 5 комментариев
Теги:
memory-management

2 ответа

2

Удивительно, что вы видите увеличение памяти, поскольку вы не инициализируете (или не прикасаетесь) к массиву. С другой стороны, неудивительно, что в Диспетчере задач вы не видите использование памяти в дальнейшем. Но об этом не о чем беспокоиться.

Почему это так?

Когда вы объявляете такой массив, он имеет автоматическую продолжительность хранения. Это означает, что для него выделяется пространство в стеке (формально, язык C++ не знает ничего такого, как "стек", но именно так все реализации, по крайней мере, все реализации, о которых я когда-либо слышал,).

Выделение пространства в стеке - это просто уменьшение указателя на некоторое значение. Пока вы не пытаетесь читать или записывать в указанную память, вы можете в значительной степени сделать что-либо с указателем без каких-либо действий (ну, конечно, что-то происходит, но не что-то захватывающее).

На низком уровне память управляется на страницах (обычно 4 килобайта), которые отображают ряд виртуальных адресов с некоторыми правами доступа к физической ОЗУ некоторым непрозрачным способом. Операционная система гарантирует, что вы никогда не узнаете об этом. Теперь операционные системы намеренно устанавливают страницу, следующую за последней страницей стека, на наличие недопустимого режима доступа, поэтому всякий раз, когда вы пытаетесь прочитать или записать значение с этой страницы, возникает сбой 1. В этом случае ОС проверяет, превышен ли максимальный размер стека (в этом случае ваша программа завершена). Если это не так, ОС фиксирует новую страницу, добавляет ее в свой рабочий набор и продолжает свою программу.

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

Теперь то, что происходит, когда вы выделяете массив из сотни тысяч double заключается в том, что указатель стека перемещается вниз на 800 000 байт (при условии, что "обычный" размер составляет 8 байтов для double). Если вы также инициализируете массив, или если вы касаетесь любой другой переменной, которую вы объявляете после этого массива, произойдет ошибка страницы, ОС будет запущена и распределит пространство стека. Process Explorer покажет это.

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

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


1 В Windows для этого есть специальный термин: защитная страница
2 В принципе, это возможно, но нет простой возможности для ОС рассказать, когда безопасно выбрасывать страницы, не требуя от компилятора вставки дополнительных системных вызовов, а просто держать страницы и заменять их в худшем случае. отлично и намного проще.
0

Определенно использовать новые и удалять для любого значительного объема выделения памяти; скажем, для чего-либо более 1 Кбайта или всего, что может использовать более 1 Кбайта памяти - если это входит в класс, вам, вероятно, нужно запустить локальную статическую переменную, чтобы подсчитать количество экземпляров (int my_class :: static_variable;) и дать каждому экземпляру класса уникальный идентификатор и флаг выделения памяти, чтобы вы могли отслеживать, кто получает выделенную память, кто не хочет и какая память освобождается - тогда вы будете наблюдать ожидаемое увеличение и уменьшение потребления памяти в Задаче Менеджер; Я отвлекся. Не имеет ничего общего с дисковыми дисками.

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню