Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Подземелье Магов
  
 

Фильтр по датам

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
 
 22:11 Ivax
 
 
Во Флориде и в Королевстве сейчас  22:28[Войти] | [Зарегистрироваться]

Организация автоматической сборки мусора для простых динамических переменных в Delphi.

Максим
дата публикации 21-11-2006 03:15

Организация автоматической сборки мусора для простых динамических переменных в Delphi.

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

Автоматическая сборка мусора для динамических переменных не является стандартом Паскаля и, в частности, в Delphi отсутствует. Существует широко известный способ организовать автоматическую сборку мусора для классов с использованием интерфейсов, однако когда речь идёт о небольших динамически выделяемых объёмах данных, для которых инкапсуляция методов не требуется (PItemIDList'ах, например), то создание класса и интерфейса для каждого подобного типа вместо простого типизированного указателя снижает быстродействие программы и усложняет её, и вообще напоминает стрельбу пушкой по воробьям. Столкнувшись с необходимостью организации автоматической сборки мусора для простых динамических переменных (не классов), я пролистал пару учебников по Delphi и провёл поиск в интернете (правда, весьма поверхностный), но решения нигде не нашёл. Так что пришлось придумать способ самому, и если он кому-нибудь окажется полезен, что ж - я буду рад :)

Предлагаемый метод основан на том, что в Delphi существует тип данных, для которого реализована автоматическая сборка мусора - это AnsiString. По сути, AnsiString - это указатель на динамически выделяемую область памяти, в которой расположен массив элементов типа char. Кроме этого массива, в выделенной области памяти присутствуют также размер выделенной области памяти (это есть в любой динамически переменной в Delphi) и счётчик количества ссылок на память (это есть только у строк). При присваивании переменной типа AnsiString s1:=s2, счётчик количества ссылок области памяти, на которую указывала переменная s1, уменьшается на 1, затем s1 устанавливается на область памяти, на которую указывает s2, и происходит увеличение счётчика количества ссылок в области s2 на 1. То же самое происходит и при передаче строк как параметров процедур и при возвращении результатов функций строкового типа. Если в ходе этих операций счётчик ссылок какой-то области памяти обнулялся, то она автоматически освобождается. Такой механизм автоматической сборки мусора (в англоязычной литературе известной как Reference Counting) позволяет очень гибко организовать работу со строками в Delphi. В самом деле, все, кто интенсивно использует строки, знают, что их можно в большинстве случаев безопасно использовать как обычные, не динамические, переменные. Никаких утечек памяти или Access Violation'ов при этом не возникает. Другим преимуществом такого метода является тот факт, что строки с одинаковым содержанием не дублируются - вместо этого они указывают на одну область памяти. Это экономит ресурсы и увеличивает быстродействие программы.

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

Итак, организуем, к примеру, тип данных Вектор:
Type
  TVector=record
    x,y,z:single
  end;
  PVector=^TVector;

Создадим теперь новый тип данных, который будет являться указателем на TVector с автоматической сборкой мусора:

Type
  APVector=object	//aka AutoPointer to Vector :)
    private
      Storage:AnsiString;
      Procedure Allocate;
    public
      Procedure Deallocate;
      Function Ptr:PVector;
      Function RefCount:integer;
  end;

Procedure APVector.Allocate;
  begin
    SetLength(Storage,SizeOf(TVector));
  end;
Procedure APVector.Deallocate;
  begin
    Storage:='';
  end;
Function APVector.Ptr:PVector;
  begin
    if Storage=''
      then
        Allocate;
    result:=PVector(Storage)
  end;
Function APVector.RefCount:integer;
  begin
    if Storage=''
      then
        result:=0
      else
        result:=integer(pointer(integer(Storage)-8)^);
  end;
Рассмотрим некоторые особенности созданного типа данных:
  1. Оператор присваивания. Пусть имеются две переменные v1,v2:APVector. Тогда присваивание v1:=v2 сводится к присваиванию v1.Storage:=v2.Storage. При этом используются все преимущества автоматической сборки мусора, реализованной в строках. То же самое относится не только к присваиванию, но и к передаче параметров типа APVector в процедуры и возвращению результатов функций типа APVector.
  2. Явное выделение памяти для переменной типа APVector невозможно. Вместо этого она выделяется при первом обращении к полям данных. Запутаться с размером выделенной области невозможно.
  3. Если есть два тина данных, реализующих автоматическую сборку мусора данным методом, то присваивание переменной одного типа значения другого типа не возможно. Таким образом для данной конструкции обеспечивается строгая типизация.
  4. Доступ к полям данных производится через функцию APVector.Ptr, возвращающую вполне определённый тип данных. Таким образом, строгая типизация присутствует и на этапе разыменования указателя.
  5. Наличие автоматической сборки мусора не исключает ситуации, когда может понадобиться вручную уменьшить количество ссылок - например, при деинициализации записи, содержащей поля типа APVector. Специально для таких случаев предусмотрена процедура APVector.Deallocate.
  6. Количество ссылок на выделенную область памяти возвращается функцией APVector.RefCount (в основном, только для отладки).
  7. Переменная типа APVector содержит только одно поле Storage, являющееся, по сути дела, указателем. Таким образом, переменные типа APVector сами по сути являются указателями, работа с ними ничуть не медленнее, чем работа с обычными pointer'ами (точнее, медленнее на самую малость из-за того, что вертятся счётчики указателей), и занимают они столько же места.

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

В принципе, в такой объект можно при желании ввести дополнительную функциональность, учитывающую необходимые особенности создаваемого типа данных. При этом полезно будет иметь в виду некоторые тонкости реализации object'ов и строк в Delphi. А именно:
  1. Если APVector является наследником другого базового объектного типа и поле Storage объявлено в предке, то при присваивании механизм подсчёта ссылок не работает, автоматическая сборка мусора, соответственно, тоже. Является ли это особенностью языка или багом, я не знаю.
  2. Функции, возвращающие результат типа APVector, возвращают правильное значение в Delphi 7 и не правильное (nil) в Delphi 3. Очевидно, этот баг был исправлен в одной из промежуточных версий Delphi, в какой именно я не знаю. При использовании Delphi 3 такие функции необходимо оформлять как процедуры с var-параметрами.
  3. Тип WideString, в отличие от AnsiString, не обладает механизмом подсчёта ссылок, так что его использовать не получится.


К материалу прилагаются файлы:


Смотрите также материалы по темам:
[Управление динамической памятью]

 Обсуждение материала [ 01-10-2009 05:08 ] 20 сообщений
  
Время на сайте: GMT минус 5 часов

Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
Функция может не работать в некоторых версиях броузеров.

Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

 
© При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
Все используемые на сайте торговые марки являются собственностью их производителей.

Яндекс цитирования