Inta[ n ]; // ошибка, размер нельзя вычислить во время компиляции
int* p = new int[ n ]; // ок, используется значение во время выполнения
delete[] p;
}
Каждое динамическое выделение, помимо запрашиваемого пользовательского объема, расходует некоторый дополнительный объем служебных данных, размер которого сильно варьируется в зависимости от среды разработки, ее версии и конфигурации сборки. Например, Visual Studio 2010 в отладочной конфигурации дополнительно к запрашиваемой памяти выделяет еще 36 байт для внутренних нужд отладчика. Соответственно, при слишком малых объемах выделения удельный вес служебной памяти может превышать удельный вес полезной памяти.
П |
Служебные данные 36 байт |
Объект 12 байт |
П |
Служебные данные 36 байт |
Объект 830 байт |
Маленький объект - служебных данных больше, чем нужных! |
Большой объект - служебных данных меньше, чем нужных |
Случайное повреждение служебной части динамически выделенной памяти также может привести к фатальному завершению программы, при этом обнаружить момент повреждения очень сложно, поскольку проблема обычно проявляется в момент освобождения памяти, что значительно позже момента повреждения.
При написании программ с повышенными требованиями к производительности к динамическому выделению памяти следует подходить очень внимательно. Обычно динамическое выделение памяти работает эффективнее для крупных блоков данных по сравнению с мелкими. Кроме того, выделение и освобождение памяти требует немалой порции времени, что может быть очень заметным в крупных программах. Временная доля для выделения+освобождения динамической памяти часто превышает 30% от общего времени выполнения.
Если память, которая более не используется программой, не освобождать, возникает явление утечки памяти (memory leak). Выделенный блок помечен распределителем динамической памяти как используемый, однако в программе не содержится ни одной переменной-указателя, хранящей его адрес, что делает его недоступным. В результате, блок никак не используется до завершения выполнения программы. Он не освобождается ни для других программ, ни для других блоков внутри данной программы.
intmain ()
{
int* p = new int[ 10000000 ]; // Выделить выделили, а освобождать кто будет?
...
}
Для маленьких программ это явление возможно не имеет огромного значения, однако для больших программ, особенно для работающих в режиме 24x7, таких как веб-серверы, наличие значительного объема утечек памяти рано или поздно приводит к ее исчерпанию, а значит к последующему фатальному завершению.
Куча |
Утечка 1 |
Утечка 2 |
Утечка 3 |
Все, что осталось от памяти |
Обнаружение утечек памяти является весьма нетривиальной задачей, требующей специальных инструментов для трассировки выделения и освобождения. Наилучшими методами предотвращения утечек памяти является аккуратность разработчика, а также применение “умных” указателей (std::unique_ptr, std::shared_ptr). Начинающих программистов, недостаточно аккуратно следящих за освобождением памяти, часто сравнивают с людьми, которые не моют за собой посуду после приема пищи - рано или поздно чистые тарелки заканчиваются.
При интенсивной работе с кучей, имеет место также более сложное явление фрагментации, когда распределитель памяти не может выделить цельный блок стоящий подряд, хотя имеется достаточное общее свободное пространство в виде нескольких меньших блоков.
Куча (1Gb всего, 350Мб свободно) |
Занято 170Мб |
Занято 130Мб |
Занято 350Мб |
Свободно 200Мб |
Свободно 150Мб |
Необходимо 220Мб |
220 < 350но цельного свободного блока в наличии нет из-за фрагментации кучи! |
В программах, сталкивающихся с такими сложностями, обычно реализовывают полностью собственную ручную логику распределения динамической памяти. Память выделяется крупными блоками, а алгоритм дальнейшего распределения оптимизируется под нужды конкретной задачи.
Дата добавления: 2016-01-29; просмотров: 1357;