Как избежать освобождения объектов, хранящихся в контейнерах с одинаковым количеством ссылок

#c #containers #programming-languages #refcounting

#c #контейнеры #программирование-языки #пересчет

Вопрос:

Я работал над некоторыми функциями пользовательского языка программирования, написанного на c. В настоящее время я работаю над системой, которая выполняет подсчет ссылок для объектов на языке, которые в c представлены в виде структур, среди прочего, с подсчетом ссылок.
Также существует функция, которая может освободить все выделенные в данный момент объекты (скажем, перед выходом из программы для очистки всей памяти). Теперь проблема именно в этом.

Я думал о том, как сделать это лучше всего, но я столкнулся с некоторыми проблемами. Позвольте мне немного обрисовать ситуацию:

выделяются 2 новых целых числа. оба имеют количество ссылок 1
выделяется 1 новый список, также с количеством ссылок 1

теперь оба целых числа попадают в список, что дает им количество ссылок 2
после этих действий оба целых числа по какой-то причине выходят из области видимости, поэтому их количество ссылок уменьшается до 1, поскольку они все еще находятся в списке.

Теперь я закончил с этими объектами, поэтому я запускаю функцию для удаления всех отслеживаемых объектов. Однако, как вы могли заметить, и список, и объекты в списке имеют одинаковое количество ссылок (1). Это означает, что нет способа решить, какой объект освободить первым.

Если я освобожу целые числа перед списком, список попытается уменьшить количество ссылок на целые числа, которые были освобождены ранее, что приведет к сбою.

Если список будет освобожден до целых чисел, это уменьшит количество ссылок на целые числа до 0, что автоматически освобождает их тоже, и никаких дальнейших шагов для освобождения целых чисел предпринимать не нужно. Они больше не отслеживаются.

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

Примечание: я уже нашел один способ, который мне действительно не нравится: добавление флага к каждому объекту, указывающего, что он находится в контейнере, поэтому не может быть освобожден. Мне это не нравится, потому что это добавляет некоторую нагрузку на память к каждому выделенному объекту, и при наличии циклической зависимости ни один объект не будет освобожден. Конечно, детектор цикла мог бы это исправить, но предпочтительно я хотел бы сделать это только с подсчетом ссылок.

Позвольте мне привести конкретный пример описанных выше шагов:

 
//this initializes and sets a garbage collector object. 
//Basically it's a datastructure which records every allocated object,
//and is able to free them all or in the future 
//run some cycle detection on all objects. 
//It has to be set before allocating objects 

garbagecollector *gc = init_garbagecollector();
set_garbagecollector(gc);

//initialize a tracked object fromthe c integer value 10
myobject * a = myinteger_from_cint(10); 
myobject * b = myinteger_from_cint(10);

myobject * somelist = mylist_init();
mylist_append(somelist,a);
mylist_append(somelist,b);

// Simulate the going out of scope of the integers.
// There are no functions yet so i can't actually do it but this
// is a situation which can happen and has happened a couple of times
DECREF(a);
DECREF(b);

//now the program is done. all objects have a refcount of 1

//delete the garbagecollector and with that all tracked objects
//there is no way to prevent the integers being freed before the list
delete_garbagecollector(gc);

  

что, конечно, должно произойти, так это то, что в 100% случаев список освобождается раньше, чем целые числа.

Что было бы более разумным способом освобождения всех существующих объектов таким образом, чтобы объекты, хранящиеся в контейнерах, не освобождались до контейнеров, в которых они находятся?

Комментарии:

1. У меня немного болит голова, когда я это читаю, но я думаю, может быть, вы не можете использовать те строительные блоки, которые у вас есть, для создания того, что вы хотите. DECREF и другие функции, вы написали их сами или они из какой-то библиотеки?

2. Я все написал сам, здесь нет библиотек. Хотя, может быть, вы правы в том, что я хочу слишком многого. Мне показалось, что это действительно хорошая система, позволяющая освободить все отслеживаемые объекты, но, возможно, я прошу слишком многого.

3. хорошо, тогда самое приятное то, что вы можете спроектировать все так, как вы хотите. Плохая часть в том, что вам нужно их спроектировать. 🙂 То, что вы просите нас сделать здесь, это разработать серверную часть для вашего языка, даже не уточняя язык. 🙂

4. Причина, по которой я пытался это сделать, заключается в том, что я знаю, что у python есть такая система для своего c api с Py_FinalizeEx() и Py_InitializeEx(), где finalize также освобождает всю память, как объясняется здесь: docs.python.org/3/c-api/init.html#c.Py_FinalizeEx «В идеале, это освобождает всю память, выделенную интерпретатором Python «.

5. Когда программа заканчивается и область действия для дескриптора списка заканчивается, почему его количество ссылок не уменьшается до 0?

Ответ №1:

Это зависит от вашего намерения с:

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

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

Если цель состоит в том, чтобы просто сообщить системе «Нам больше не нужны объекты», то другой вариант — просто обойти корни и уменьшить их количество ссылок. Если на них нет других ссылок, они достигнут нуля. Затем они уменьшат количество ссылок для всего, на что они ссылаются, перед освобождением. Это, в свою очередь, проходит через граф объектов. Если корни — это единственное, что удерживает ссылки в точке, которую вы вызываете, это эффективно освободит все.

Комментарии:

1. на самом деле это умная идея! Теперь это ответ, который является именно тем, что я искал. Большое спасибо за ответ! Я совершенно упустил из виду тот факт, что пересчеты действительно больше не имеют никакого значения. забавно, что как только вы начинаете думать в одном направлении, увидеть какие-то другие варианты может стать очень трудно. еще раз, спасибо!

2. и действительно, цель как раз и состоит в том, чтобы принудительно освободить все, как вы говорите

3. Просто небольшое обновление для тех, кто снова будет создавать подобный язык и нуждается в такой системе: я решил все это, отключив вызов деструктора в макросе DECREF() при вызове этой функции free_all_objects, что делает невозможным преждевременное освобождение. Еще раз спасибо за вдохновение.

Ответ №2:

Вы не должны ничего освобождать, пока количество ссылок для somelist не станет равным нулю.

Комментарии:

1. Обычно нет, но в конце программы я просто хочу освободить все еще существующие объекты. От объектов ничего не будет зависеть, просто нужно освободить все и не вызывать segfault. При обычном выполнении этого никогда не происходит.

2. @jonathan В конце программы память — это наименьшая из ваших забот, потому что вы можете просто освободить все это. Другим ресурсам, таким как файлы, может потребоваться надлежащее завершение работы.