11.1 Пример |
Предыдущая Содержание Следующая |
Все ресурсы, приобретённые программой, должны быть правильно возвращены для повторного использования. Динамическая память — ресурс, и выпускаемые версии программы конечно же должны быть проверены на утечки памяти. В качестве примера рассмотрим, что происходит, когда мы делаем синтаксическую ошибку при использовании калькулятора, разработанного в третьей и пятой главе:
$ value (3 * 4) - - bad factor: ’’ 0x0
Алгоритм рекурсивного спуска пытается создать дерево выражения. Если что-то идёт не так, как надо, функция error() использует longjmp(), чтобы удалить всё, что находится в стеке, и продолжает выполнение основной программы. Стек, однако, содержит части дерева выражений, созданного к настоящему времени. Если есть синтаксическая ошибка, эти части теряются: у нас есть утечка памяти. Это конечно же стандартная проблема в построении интерпретаторов. NeXTSTEP предоставляет простое приложение MallocDebug, которое может использоваться, чтобы определить местоположение, по крайней мере, некоторых более серьёзных проблем. Если мы скомпонуем value с -lMalloc-Debug, стандартные версии malloc() и связанные с ней функции заменятся модулем, который сможет связаться с приложением MallocDebug. Запускаем MallocDebug после value, подключаем их друг к другу и нажимаем кнопку Leaks, как только получаем первое сообщение об ошибке. К сожалению, сообщение простое:
No nodes.
MallocDebug использует довольно наивный метод для проверки на утечки: оно имеет список всех выделенных областей памяти и просматривает слова в задаче клиента, чтобы увидеть, указывают ли они на выделенные области. Утечками памяти считаются только те области, на которые не указывает ни одно слово в задаче клиента. Для ввода
(3 * 4) - -
sum() получит первое поддерево, построенное product() прежде, чем factor() доберётся до конца строки ввода. Тем не менее, когда error() отсекает стек от factor() обратно к main(), адрес корня этого поддерева все ещё находится в локальной переменной result в sum() и по случайности не перезаписывается в longjmp(). Остающиеся узлы связаны с корневым, то есть с точки зрения MallocDebug до всех узлов всё ещё можно добраться. Однако, если мы введём другое выражение, старый стек перепишется, и MallocDebug найдет утечку. value:
$ value (3 * 4) - - bad factor: ’’ 0x0 1 + 3 4
MallocDebug:
Zone: Address: Size: Function: default 0x050ec35c 12 mkBin, new, product, sum, factor, product, sum, stmt
Если value скомпилировано с отладочной информацией, мы можем запустить отладчик во втором окне и исследовать утечку:
$ gdb value GDB is free software ... (gdb) attach 746 Attaching program `value’, pid 746 0x5007be2 in read () (gdb) print * (struct Bin *) 0x050ec35c Reading in symbols for mathlib.c...done. $1 = { type = 0x8024, left = 0x50ec334, right = 0x50ec348 } (gdb) print process(0x050ec35c) Reading in symbols for value.c...done. $3 = void (gdb)
Отладчик GNU можно подключить к работающему процессу. С помощью print можно вывести на экран содержание узла с утечкой, если скопировать адрес из окна MallocDebug и подставить надлежащий тип: mkBin() вызывает malloc(), то есть мы должны были получить struct Bin. Как показывает вывод, print может даже вызвать метод, подобный process() в value, и показать результат. Вывод из process() появляется в окне, где выполняется value:
$ value (3 * 4) - - bad factor: ’’ 0x0 1 + 3 4 12
Утечка памяти жива и здорова.
|
Предыдущая Содержание Следующая |