Борьба с утечками ресурсов и переполняющимися буферами

демонстрационный пример программы, использующей макрос auto_alloc


При работе со стековой памятью следует помнить три обстоятельства: во-первых, по умолчанию каждый поток получает всего лишь 1 Мбайт стековой памяти, что по современным понятиям очень мало. Стековую память первичного потока легко увеличить, передав линкеру ключ "/STACK:reserve[,commit]", где reserve – зарезервированная, а commit – выделенная память. Размер стековой памяти остальных потоков определяется значением аргумента dwStackSize

функции CreateThread.

Во-вторых, при старте потока Windows выделяет ему минимум страниц стековой памяти, размещая за ними специальную "сторожевую" страницу (PAGE GUARD) при обращении к которой возбуждается исключение, отлавливаемое системой, которая выделяет потоку еще несколько страниц, перемещая PAGE GUARD наверх. Если же мы попытаемся обратиться к памяти, лежащей _за_ PAGE GUARD — произойдет крах. Поэтому, при ручном выделении стековой памяти, необходимо последовательно обратиться хотя бы к одной ячейке каждой страницы: #define stack_out(p,n) for(a=0;a<n;a+=0x100)t=p[a];

В-третьих, размер выделяемых блоков памяти должен быть кратен четырем, иначе многие API и библиотечные функции откажут в работе.

Но что делать, если, несмотря на все усилия, память продолжает утекать? На этот случай у мыщъх'а припасено несколько грязных, но довольно эффективных трюков. Вот один из них: когда количество потребляемой приложением памяти достигает некоторой, заранее заданной отметки, мы "прогуливаемся по куче" API-функцией HeapWalk, сохраняя все выделенные страницы в специальный файл (устроенный по принципу файла подкачки) и возвращаем память системе, оставляя страницы зарезервированными и назначая им атрибут PAGE_NOACCESS. После чего нам остается только отлавливать исключения и подгружать содержимое реально используемых страниц, восстанавливая оригинальные атрибуты доступа (PAGE_READ или PARE_READWRITE). В результате, утекать будет только адресное пространство, которое, между прочим не бесконечно, и при интенсивной течи довольно быстро кончается. И что же тогда? Можно, конечно, просто завершить программу, но лучше рискнуть и попробовать освободить блоки к которым дольше всего не было обращений. Разумеется, мы не можем гарантировать, что именно они ответственны за утечку памяти. Быть может, программа в самом начале выделила несколько блоков, планируя обратиться к ним при завершении процесса, но… риск благородное дело!



Содержание раздела