Сергей Боровков » 01.11.2008 13:59:17
Как мы раскопали с Сергеем Смирновым, менеджер памяти округляет выделяемый размер в большую сторону до кратного 64к (назовем блоком). Пусть мы создаем TMemoryStream в 40'000'000 байт. Менеджер памяти выделяет блок 40042496 байт, который делит на два куска - 40'000'016 ( там есть заголовок 16 байт+округление до 16 байт) и кусок-остаток. Далее первый кусок используется TMemoryStream'ом. Но за время использования блока программа хочет выделить какую-то мелочь (для той же отрисовки), даже меньше килобайта легко... Прикол же состоит в том, что стандартный TMemoryManager при попытке выделить память, искать начинает с куска, который _последним_ зарегистрировался как свободный. А последним в нашем случае зарегистрировался кусок от выделенных округленных 40 мегабайт (выдеял-то TMemoryManager не 40'000'016, а 40042496). Итог - когда TMemoryStream освобождаем, то выясняется, что блок, размером в 40 мегабайт в результате не освобождается, поскольку в конец к нему, в остаток, засел маленький кусочек, который не дает освободить 40 мег.
Если мы сразу после этого попробуем взять 40'000'000, то TMemoryManager честно нам предоставит наши 40'000'000, выделенные раньше. Но до этого время нам потребуется хотя бы 1 байт, то этот один байт (округленный до 16 байт+заголовок 16 байт, но это мелочи) будет выделен из куска, который последним отметился как свободный. А последним освобожденным куском у нас является... ? правильно, 40 мегабайтный блок. Этот 40 мегабайтный блок делится на две части, 32 байта (округление, заголовок) и весь остаток. И когда нам потребуется выделить еще 40 мег памяти, прога честно съест еще 40 мег от системной памяти.
Правила особождения полностью свободных блоков: блок особождается если количество свободных блоков превышает 4 или 3 (от версии зависит), либо если размер блока превышает некую величину (мегабайт). То есть большие блоки освобождаются всегда, как только становятся полностью свободными.
Таким образом данный менеджер памяти расчитан на то, что, либо программа работает с постоянно меняющимися данными (то есть старые куски освобождаются полностью и менеджер памяти их освобождает с точки зрения системы), либо - программа работает с памятью в режиме, похожим на стек, нужно произвести какую-либо операцию, прога сначала взяла 100 кусков по мегабайту, а потом все освободила.
Неэффективно же этот менеджер памяти работает при выделениях памяти очень разного размера, при уменьшении размера выделяемых блоков, а также при выделении кусков памяти не кратных 65536 байтам. Последние два пункта впрямую ведут к большой фрагментации памяти и ее последовательному потреблению программой.
Методы решения - выделять куски, кратные 65536, не уменьшать размер выделенной памяти, либо что-то сделать с менеджером памяти.