Объектное управление памятью

API управления памятью для GObjects немного усложнен, но сама идея довольно проста: целью является обеспечить гибкую модель основанную на подсчете ссылок которая может интегрироваться в приложения использующие или требующие разных моделей управления памятью (такие как сборка мусора (garbage collection), aso...). Методы которые используются для манипулирования подсчётом ссылок описаны ниже.

/* Подсчёт ссылок */ gpointer g_object_ref (gpointer object); void g_object_unref (gpointer object); /* Слабые ссылки */ typedef void (*GWeakNotify) (gpointer data, GObject *where_the_object_was); void g_object_weak_ref (GObject *object, GWeakNotify notify, gpointer data); void g_object_weak_unref (GObject *object, GWeakNotify notify, gpointer data); void g_object_add_weak_pointer (GObject *object, gpointer *weak_pointer_location); void g_object_remove_weak_pointer (GObject *object, gpointer *weak_pointer_location); /* Обработка цикла */ void g_object_run_dispose (GObject *object);

Подсчёт ссылок

Функции g_object_ref/ g_object_unref соответственно увеличивают и уменьшают количество ссылок. Эти функции потоко-безопасны начиная с версии GLib 2.8. Подсчёт ссылок, что не удивительно, инициализируется с помощью g_object_new которая означает что вызывающая программа - текущий единственный владелец вновь созданной ссылки. Когда количество ссылок достигло нуля, то есть когда g_object_unref вызвал последний клиент содержащий ссылку на объект, вызываются dispose и finalize методы класса.

Наконец, после вызова finalize, вызывается g_type_free_instance для освобождения экземпляра объекта. В зависимости от политики распределения памяти при регистрации типа (через одну из функций g_type_register_*), объектный экземпляр памяти будет либо освобождён либо возвращён в пул для этого типа. Как только объект освобождён, если он был последним экземпляром типа, типовой класс будет уничтожен как описано в “Instantiable classed types: objects” и “Non-instantiable classed types: Interfaces.”.

Таблица ниже резюмирует процесс уничтожения GObject:

Таблица 5. g_object_unref

Время вызова Вызываемая функция Параметры функции Ремарка
Последний вызов g_object_unref для экземпляра целевого типа Располагающая функция класса целевого типа Экземпляр GObject Когда расположение завершено, объект не должен содержать ссылки на любые другие члены объекта. Объект также, как ожидается, будет способен ответить на вызывающий метод клиента (возможно кодом ошибки но не нарушая память) в течение процесса завершения. Расположение может выполняться больше одного раза. Расположение должно привязываться к его родительской реализации как раз перед возвращением в вызывающую программу.
функция завершения класса целевого типа Экземпляр GObject Финализация завершает процесс инициализированный расположением (dispose). Она завершает уничтожение объекта. Финализация выполняется только один раз. Финализация должна привязываться к её родительской реализации как раз перед возвратом в вызывающую программу. Причина разделения процесса уничтожения на две части объясняется в “Reference counts and cycles”.
Последний вызов g_object_unref для последнего экземпляра целевого типа Интерфейсная функция interface_finalize В интерфейсной vtable Практически не используется. Маловероятно что вам это понадобится.
Интерфейсная функция base_finalize В интерфейсной vtable Практически не используется. Маловероятно что вам это понадобится.
Функция class_finalize целевого типа В структуре класса целевого типа Практически не используется. Маловероятно что вам это понадобится.
Типовая функция base_finalize В дереве иерархии классов из базового типа в целевой тип. base_init вызывается один раз для каждой сструктуры класса. Практически не используется. Маловероятно что вам это понадобится.


Слабые ссылки

Слабые ссылки используются для контроля процесса финализации (finalization): g_object_weak_ref добавляет контрольный вызов который не содержит ссылок на объект, но который вызывается когда объект запускает свой метод размещения. Так же, каждая слабая ссылка может быть вызвана более одного раза в течение финализации объекта (так как размещение может выполняться больше одного раза в течение финализации объекта).

g_object_weak_unref может использоваться для удаления контролирующего вызова из объекта.

Слабые ссылки так же используются для реализации g_object_add_weak_pointer и g_object_remove_weak_pointer. Эти функции добавляют слабые ссылки на объект, они используются для аннулирования указателя полученного пользователем когда объект финализирован.

Подсчёт ссылок и циклы

Заметьте: следующий раздел был написан благодаря James Henstridge. Я надеюсь что все ваши благодарности и проклятия будут адресованы непосредственно ему.

Модель управления памятью GObject была спроектирована для более лёгкой интеграции в существующий код используя "сборку мусора". Именно поэтому процесс уничтожения разделён на две фазы: первая фаза, выполняется в обработчике размещения и должна освободить все ссылки на другие объекты. Вторая фаза, выполняется обработчиком финализации и должна завершить процесс уничтожения объекта. Объектные методы должны быть способны выполняться без программных ошибок (то есть, без segfault :) между двумя фазами.

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

Внимательные читатели возможно поняли одно из правил обработчика размещения которое мы заявили немного раньше: обработчик размещения может быть вызван много раз. Предположим мы имеем цикличный подсчёт ссылок: объект A ссылается на B который в свою очередь ссылается на объект A. Предположим мы обнаружили цикл и нам необходимо уничтожить оба объекта. Один из способов сделать это - вызов g_object_dispose для одного из объектов.

Если объект A освободит все ссылки на другие объекты, значит он освободит ссылку на объект B. Если объект B не принадлежит ни кому, значит это его последняя ссылка что означает выполнение обработчика размещения объекта B, который в свою очередь освободит ссылку на объект A. Если это последняя ссылка A, запустится обработчик размещения A, который выполнится второй раз прежде чем будет вызван обработчик финализации A !

Пример выше, который может показаться немного замысловатым, действительно может произойти если ваш GObject обрабатывается языковой привязкой. Таким образом я предполагаю что правила описанные ранее для уничтожения объекта соблюдаются. Иначе, могут произойти Очень плохие вещи.