11.1 Пример

Предыдущая  Содержание  Следующая V*D*V

Все ресурсы, приобретённые программой, должны быть правильно возвращены для повторного использования. Динамическая память — ресурс, и выпускаемые версии программы конечно же должны быть проверены на утечки памяти. В качестве примера рассмотрим, что происходит, когда мы делаем синтаксическую ошибку при использовании калькулятора, разработанного в третьей и пятой главе:

 

$ 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

 

Утечка памяти жива и здорова.

 

Предыдущая  Содержание  Следующая