никак. Сборка у меня только для _данных_ A, B, и C. И все эти данные выделяются например оператором GC::new. Сами объекты классов-наследников GC НЕ управляются GC, как и сам базовый класс GC.
Или ты настолько упорот, что хочешь создать GC, который управляет объектами GC? Т.е. сам собой?
ну да, в Java нужен, а в C++ значит, такой же — не нужен.
В Java он используется, просто потому, что он уже есть для остального и нет никакого смысла прикручивать что-то еще. Это простейший случай владения, с которым С++ прекрасно справляется.
GC это _алгоритм_ управление памятью
GC - это сборщик мусора. Там несколько стадий со своими алгоритмами. Кроме того, у сборки мусора много разновидностей. Но то, что ты показываешь, это просто RAII.
GC с момента своего появления работает не просто с памятью, а с графом объектов. Так было в первом Лиспе с GC, так сейчас в JVM, .NET, etc.
GC - это сборщик мусора. Там несколько стадий со своими алгоритмами. Кроме того, у сборки мусора много разновидностей. Но то, что ты показываешь, это просто RAII.
покажи мне «не просто RAII».
ЗЫЖ я прекрасно знаю про «множество разновидностей», потому и не могу тебе вот сейчас взять, и отлить серебряную пулю для всего.
В C++ разновидностей ещё больше, ибо ты не ограничен тем, что тебе дали.
Или ты настолько упорот, что хочешь создать GC, который управляет объектами GC?
Это ты зачем-то наследуешь классы от сборщика мусора. Не надо перекладывать с больной головы на здоровую.
никак.
Тогда и сборки у тебя нет. И да, такая «сборка» уже есть в стандартной библиотеке. Причем в более обобщенном и красивом виде. std::unique_ptr, std::shared_ptr, std::weak_ptr + свой аллокатор и все. Но я тебе еще раз говорю, это не полноценная сборка мусора.
Да тебе миллион раз показывали примеры. Ты придирался к обозначениям в псевдокоде. К ref, к <> и пр. И даже когда это все убрали - просто ушел от ответа и начал говорить, что объект A не в куче, хотя автор примера тебе сказал, что именно там.
Это ты зачем-то наследуешь классы от сборщика мусора.
это для того, что-бы сборщик мусора мог управлять разными объектами. Вот класс A у меня работает со строками, ну типа твоего String'а. И в классе A есть специфические операторы, вроде operator+, который реализует конкатенацию строк. Но с памятью он НЕ работает. Вообще. Память выделяет/освобождает базовый класс GC, который понятия не имеет о строках A, и для которого «строка» — просто набор байтов. Он знает их число, знает их адрес, и знает, КОМУ они принадлежат (ибо он — базовый класс. Ему и принадлежат)
Тогда и сборки у тебя нет. И да, такая «сборка» уже есть в стандартной библиотеке. Причем в более обобщенном и красивом виде. std::unique_ptr, std::shared_ptr, std::weak_ptr + свой аллокатор и все. Но я тебе еще раз говорю, это не полноценная сборка мусора.
это вообще другое. Набор гаечных ключей ничем не лучше и не хуже набора отвёрток. А про «красиво» — это ты не по адресу.
И даже когда это все убрали - просто ушел от ответа и начал говорить, что объект A не в куче, хотя автор примера тебе сказал, что именно там.
автор примера — <нечто обидное>. Откуда такая узость мышления? Почему в Java объектом может быть что угодно, а в C++ только глупый указатель/ссылка? Почему в этом ВАШЕМ C++ сборщик мусора может оперировать только непосредственно с переменными самого ЯП, а не через некий интерфейс?
Вот класс A у меня работает со строками, ну типа твоего String'а. И в классе A есть специфические операторы, вроде operator+, который реализует конкатенацию строк. Но с памятью он НЕ работает. Вообще. Память выделяет/освобождает базовый класс GC, который понятия не имеет о строках A, и для которого «строка» — просто набор байтов.
В стандартной библиотеке С++ за это же самое отвечают аллокаторы. Это GC, да?
И как внешний сборщик будет строить граф объектов, определять рутовые ссылки и пр.? Сборщик мусора работает не просто с памятью, а с графом объектов.
0. собираемые gc ссылки назовем сильными ссылками, на с++ они будут HardRef<T>
1. картой памяти назовем битовую карту, где бит i установлен, если по адресу 4*i лежит 4-ка байт, представляющая собой HardRef<T>; эти биты будут устанавливаться в момент конструирования HardRef<T> его конструктором (для простоты говорим о 32 битах; в 64 почти то же самое)
2. именно граф объектов, кажется, строить не обязательно, но если уж мы собрались перемещать объекты, то пусть T обязан иметь таблицу виртуальных функций; тогда можно узнать sizeof(T) либо через компиляторную магию, либо через требование для T иметь в виде предка че-то-там с методом size_of_me(); дальше смотрим установленные биты в карте памяти, и трассируем эти указатели
3. рутовые ссылки: пусть все my_hard_ref->payload аллоцируются в определенной арене; тогда рутовые ссылки будут все те, у кого установлен бит в карте памяти, но лежит вне арены
4. перемещение: делается стандартно, кажется это называется self-references в uniprocessor garbage collection technics или как там его
насчет тормозов: да, карта памяти объекта будет делаться нерационально, но и тут можно подставить костылики; насчет карты памяти фрейма стека функции — ты прям вот уверен, что она всегда одна и та же? я — нет
виртуальный T::size_of_me() похоже можно сделать через костыли на стандартном с++, ну или совсем чуть-чуть пропатчить компилятор, но вообще, конечно, поддержка со стороны компилятора не помешала бы
например, компилятор с++ будет разрешать иметь обычные ссылки на поля объекта, который создан через HardRef
в общем, по-хорошему нужен нормальный язык
да, еще KblCb спрашивал «Каким образом он разберётся?» — ответ комментом выше
картой памяти назовем битовую карту, где бит i установлен, если по адресу 4*i лежит 4-ка байт, представляющая собой HardRef<T>; эти биты будут устанавливаться в момент конструирования HardRef<T> его конструктором (для простоты говорим о 32 битах; в 64 почти то же самое)
зря ты про эту схему вспомнил ИМХО. Очевидно жеж, что она ненадёжна.
3. рутовые ссылки: пусть все my_hard_ref->payload аллоцируются в определенной арене; тогда рутовые ссылки будут все те, у кого установлен бит в карте памяти, но лежит вне арены
Можно чуть подробнее на этом месте? Я пока не сообразил что ты имеешь в виду.
Отлично. Кстати, несколько похоже на то, что я обдумывал, только у меня был список ссылок, а не битовая карта. С помощью битовой карты (кажется) можно обходить поля-ссылки, не строя никаких графов и достаточно быстро.
И еще я не догадался, что все вне арены можно считать рутом. Это просто гениально, мне кажется =)
пусть T обязан иметь таблицу виртуальных функций; тогда можно узнать sizeof(T) либо через компиляторную магию, либо через требование для T иметь в виде предка че-то-там с методом size_of_me(); дальше смотрим установленные биты в карте памяти, и трассируем эти указатели
Хм. Вот я, например, предполагал получать размер при создании и хранить вместе с объектом. Если мы делаем какую-нибудь gc_new<A>(x,y,z), то мы делаем ее шаблонным и имеем всю информацию о типе, какую можно получить на статике. Если же мы переопределяем new или делаем свой аллокатор, то просто считаем объектом весь запрашиваемый объем памяти(а размером - переданный нам размер). Разве этого не достаточно?
перемещение: делается стандартно, кажется это называется self-references в uniprocessor garbage collection technics или как там его
В контексте С++ приходится накладывать ограничение на указатели на члены, на внутренние сырые и не очень указатели на поля и пр. и пр. Я не знаю, как запретить такие вещи даже с помощью шаблонной магии... Потому, честно говоря, не уверен, стоит ли вообще делать перемещающий сборщик для С++. Впрочем, это можно сделать лишь одной из его стратегий.
В контексте С++ приходится накладывать ограничение на указатели на члены, на внутренние сырые и не очень указатели на поля и пр. и пр.
ты всё делаешь не так.
1. делаем все объекты на стеке (на самом деле не обязательно, мне так проще описывать)
2. корневая ссылка создаётся в конструкторе по умолчанию
3. есть список корневых ссылок (не обязательно это список)
4. оператор/конструктор копирования создаёт некорневую ссылку
5. когда объект уходит из области видимости, он становится мусором. Если это корневая ссылка, она замещается какой-то своей некорневой, если некорневых нет, то удаляется из списка корневых
6. можно делать массивы. Для этого перезагружаются []. Массивы могут индексироваться любыми объектами. И содержать любые объекты. operator[] отдаёт умный указатель, которому можно присваивать, и который можно использовать вместо объекта.
7. можно делать списки. Если к примеру для integer определён print(), то list->head->print() тоже работает, за счёт перезагрузки ->. (селектор класса тоже можно перезагружать, причём многократно)
8. можно делать всякие другие групповые объекты. (деревья и проч.)
9. массивы C++, глупые указатели, и проч. просто НЕ ИСПОЛЬЗУЮТСЯ.
он думает: Ага! Это объект X какого-то класса, и берётся его поле Y по смещению от адреса в памяти X! Сборка мусора невозможна!
На самом деле, это никакой НЕ адрес и НЕ смещение. "->" это ФУНКЦИЯ. И нужна она здесь для того, что-бы X вёл себя как Y. Т.е. делегировал свой функционал Y. Если Y это строка, то и X->Y тоже строка, и с ней можно делать тоже самое, что с любой строкой. В том числе, GC может сдвинуть её данные выше на 12783 байтов. Потому-что это никакое не смещение, ни какого адреса.
forCe вроде как раз плюсовик и сишник. А 'тролль' как раз по делу все написал и честно сказал, что у с++ тут есть ряд проблем, которые мог бы решить компилятор
А 'тролль' как раз по делу все написал и честно сказал, что у с++ тут есть ряд проблем, которые мог бы решить компилятор
проблема тут всего одна: когда мы смотрим на сишный указатель(ссылку), мы видим 0x234DF3430. ВСЁ. Да, без libaastral'а нет никакой возможности выяснить, что эта за хрень, откуда она взялась, и даже её размер непонятен (ибо это может быть массив, одиночный эл-т, и даже голова списка/дерева. Sizeof даст размер одного эл-та по любому)
Я вам уже сказал, что надо всё наследовать от общего предка, который и будет следить(и запоминать) за тем, что откуда взялось. Тогда при запуске GC, мы можем это всё восстановить, и выяснить, что уже мусор, а что — нет. Можем и больше — можем циклы, можем нелинейные графы, можем поколения, можем вообще ВСЁ.
«зависших» ссылок у нас не получается потому, что operator=() перегружен. И мы никак не можем затереть и повесить ссылку.
раньше освободить объект тоже не получается, ибо delete и деструктор тоже перегружены, и НЕ станут возвращать в кучу указатель, который ещё используется.
Т.е. мы можем сделать php::unset(obj); который создаёт новый пустой obj на месте старого. При этом кажется, что obj «очистился», но это не так. На самом деле, этот объект стал не нужным 'obj'. Если он больше никому не нужен, мы его можем сразу удалить, а можем оставить это GC.
И обмазываться глупыми указателями совсем не обязательно, и даже не нужно. Но можно, да. Никто не запрещает, это не java, и тут вам никто не мешает отстрелить себе яйца.
Так предложи достаточно умные. А то ты предлагаешь кучу ограничений, т.е. фактически использование твоего GC не просто не даст преимуществ, а будет хуже ручного управления памятью.
Ты пока даже не привел дельного использования своего «GC», что в купе с весьма мутным описанием и каким-то списком ограничений, дает основания предполагать, что с любого примера ты съедешь, как и со всех примеров, приводимых тебе в этой теме. Поэтому давай ты приведешь какой-нибудь фрагментик использования объектов твоего GC.
А пока я тебе отвечу, что любые объекты в куче, содержащие в себе ссылки на другие объекты в куче и т.д. использовать в твоем «GC» будет очень неудобно, если вообще возможно...
картой памяти назовем битовую карту, где бит i установлен, если по адресу 4*i лежит 4-ка байт, представляющая собой HardRef<T>; эти биты будут устанавливаться в момент конструирования HardRef<T> его конструктором (для простоты говорим о 32 битах; в 64 почти то же самое)
Для 64 битов полная битовая карта потребует несколько сотен миллионов гигов памяти(лень точно считать). Ты уверен, что хочешь этого? Для 32 вроде нормально, 64 мега.