Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Сборщик мусора vs Умный указатель

350
02 сентября 2007 года
cheburator
589 / / 01.06.2006
Есть уже реализованные "умные указатели", удаляющие объект гарантированно и когда нужно - к примеру, boost::shared_ptr (здесь и далее - речь о С++). Но в некоторых средах разработки и языках имеются "сборщики мусора", уничтожающие ненужные объекты, скажем, в тот момент, когда машина не очень занята важными делами, за счет чего можно ускорить работу программы (деструкторы и функции освобождения объектов вызываются лишь в момент сборки мусора). Но на С++ я не встречал сборщиков мусора (а есть ли они?).

Так вот, недавно в голову пришла идея - реализовать такой сборщик. Создаем класс, очень схожий с shared_ptr, но с небольшими отличиями - наш класс никогда не будет уничтожать контролируемый им объект. Запускаем поток (далее - Сборщик), который будет заниматься сборкой мусора. Имеется контейнер, содержащий ссылки на все выделенные объекты.
Каждый раз при создании объекта помещаем в контейнер наш аналог shared_ptr.
Поток Сборщика срабатывает только тогда, когда машине "нечем заняться", либо при недостатке памяти в момент очередного создания объекта.
Сборщик анализирует контейнер на наличие неиспользуемых объектов и уничтожает их. Для ускорения поиска таких объектов контейнер проиндексируем по признаку отсутствия ссылок, этот признак будет выставляться нашим аналогом shared_ptr.
Как известно, в многопотоковых приложениях операция выделения памяти должна блокировать некий мьютекс на выделение места в куче (heap). Так вот, можно не создавать отдельный мютекс на блокировку контейнера выделенных объектов - вставка объекта в этот контейнер может происходить одновременно с выделением места в куче. Что касается уничтожения объектов - возникает еще одна экономия: не нужно блокировать кучу при каждом уничтожении объекта, поскольку этим занимается Сборщик, он может за один сеанс уничтожить много объектов.
Мне кажется, такая концепция в многопотоковых приложениях, часто обращающихся к куче, будет работать лучше, чем shared_ptr/scoped_ptr/auto_ptr и др...

Хотелось бы услышать мнение знатоков.
350
02 сентября 2007 года
cheburator
589 / / 01.06.2006
Можно даже отказаться от контейнера объектов и необходимую информацию хранить прямо в куче. Придется, правда, реализовать свои механизмы выделения/освобождения.
260
02 сентября 2007 года
Ramon
1.1K / / 16.08.2003
http://www.hpl.hp.com/personal/Hans_Boehm/gc/
3
02 сентября 2007 года
Green
4.8K / / 20.01.2000
Ты как-то уже поднимал подобную тема. Вот мои комментари:
http://forum.codenet.ru/showthread.php?p=136960

Дело в том, что функция сборщика не просто удалить память от неиспользуемых объектов, но и оптимизировать после этого расположение используемых объектов, т.е. провести дефрагментацию. И IMHO это его основное достоинство. Но это достоинство предполагает наличие run-time инф. о типах, возможность сериализации.

Удалять объекты лучше все же сразу после использования.
От "забывчивости" помогают умные указатели и их вполне достаточно.
Для продуктивности в многопоточных приложениях можно придумать множество решений. Например, предоставить каждому потоку свою собственную область памяти для кучи.

А вот дефрагментация - серьезная проблема, ососбенно при жестком лимите памяти. Решения конечно тоже есть, например, по-разному выделять память под большие и маленькие объекты.
350
02 сентября 2007 года
cheburator
589 / / 01.06.2006
Цитата: Green
Дело в том, что функция сборщика не просто удалить память от неиспользуемых объектов, но и оптимизировать после этого расположение используемых объектов, т.е. провести дефрагментацию.
---
А вот дефрагментация - серьезная проблема, ососбенно при жестком лимите памяти. Решения конечно тоже есть, например, по-разному выделять память под большие и маленькие объекты.


Дефрагментация, я считаю - это долго. Иногда неприемлемо. Сериализация тоже. Насчет указанных "решений" - ты, наверное, имел в виду low fragmentation heap -это удачное решение, позволяющее избежать фрагментации (не совсем, конечно, но примерно так...). К тому же, как реализована куча - обычная это куча или LFH - это концепция, независимая от умных указателей или сборщика мусора - и тот и другой можно реализовать и на той, и на другой куче.

Цитата: Green
Удалять объекты лучше все же сразу после использования.


Отчасти согласен, отчасти нет.
Почему не согласен - см. первоначальный пост, там я указал преимущества отложенного уничтожения объектов.
Согласен в том, что иногда объект должен сразу освобождать какие-то ресурсы, особенно не связанные с памятью, сразу после того, как перестает использоваться, а не тогда, когда вздумается сбощику мусора. К примеру, поверхности DirectDraw или что-то вроде того - короче, "дорогие" ресурсы.
.NET - платформа, насколько мне известно, с этой целью вместо понятия деструктора вводит два понятия - некий "клинапер", освобождающий эти дорогие ресурсы, и собственно деструктор, освобождающий "недорогие" ресурсы и связанные, подчиненные и т. п. объекты.

Цитата: Green
Например, предоставить каждому потоку свою собственную область памяти для кучи.


В общем, все правильно, но если честно, я писал первоначальный пост немного в контексте конкретной проблемы - многопоточные приложения часто выделяющие/освобождающие память, в которых потоки сильно взаимосвязаны - имеется множество "общих", "межпотоковых" объектов. Тут сложно выдать каждому потоку свою кучу.

Итог сему посту таков.
Дефрагментацию нафиг - используем LFH.
Видимо, придется изобретать отдельный "клинер" каждому объекту, и отдельно - деструктор.
Таким образом избежим частых блокировок кучи и сэкономим время в моменты загруженности приложения.
Конечно, все это верно в "контексте конкретной проблемы".

350
02 сентября 2007 года
cheburator
589 / / 01.06.2006
Цитата: Green
...особенно при жестком лимите памяти.

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

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог