Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2009.06.14;
Скачать: [xml.tar.bz2];

Вниз

Как вручную выделять и освобождать память объектов.   Найти похожие ветки 

 
Дмитрий С ©   (2009-04-07 08:25) [0]

Примерно такой код:
   GetMem(My, TMyClass.InstanceSize); // Где-то беру необходимое количество памяти
   TMyClass.InitInstance(My); // Инициализируем экз. класса
   TMyClass(My).Create;  // Выполняем нужный конструктор

   Writeln(TMyClass(My).Text); // Проверяем работоспособность экз. класса

   TMyClass(My).Free; // Вызываем деструктор
   FreeMem(My);    // Где-то потом освобождаем память

Все бы хорошо, но при выполнении выделенной строки автоматически выполняется _FreeMem и память освобождается (и не всегда правильно, ведь TObject не знает каким образом я ее выделил).

Данную проблему я решил так:
TMyClass = class(...)
 procedure FreeInstance; override;
end;
procedure TMyClass.FreeInstance;
begin
 CleanupInstance;
end;


Насколько правильно так делать?
Вообще задача состоит в том, чтобы создать сразу много экземпляров какого-либо класса, выделяя, при этом, память один раз на всех сразу.


 
Медвежонок Пятачок ©   (2009-04-07 10:06) [1]

Ну и выдели на всех сразу одним гетмемом. Только зачем впихивать в выделенную область сами инстансы?


 
oxffff ©   (2009-04-07 10:37) [2]


> Дмитрий С ©   (07.04.09 08:25)  


Есть разные способы,
например этот не требует от тебя перекрытия в тех классах для которых ты хочешь сделать данный trick.

procedure TForm1.Button1Click(Sender: TObject);
var a:Tobject;
begin
a:=Tobject.create;
asm
xor dl,dl;
mov eax,a;
mov ecx,[eax];
call [ecx+vmtOffset Tobject.destroy];

end;
a.CleanupInstance;
FreeMem(pointer(a));
end;


 
Тимохов_   (2009-04-07 16:54) [3]

А чего бы TObject.NewInstance и TObject.FreeInstance не перкрывать и там вместо GetMem/FreeMem обращение к своему манагеру памяти.


 
DiamondShark ©   (2009-04-07 17:41) [4]


> Вообще задача состоит в том, чтобы создать сразу много экземпляров
> какого-либо класса, выделяя, при этом, память один раз на
> всех сразу.

Дурная гойлова рукам покоя не даёт.


 
Тимохов_   (2009-04-07 17:58) [5]

Ну вообще задача имеет право на жизнь - если действительно много объектов, то может оказать существенное влияние на улучшение быстродействия. Только вот та ли эта задача. Надо бы сначала количественно оценить выгоду. Защени общее время выполнение операций создания твоей объектной модели - может это всего 5% от общего времени.


 
Дмитрий С ©   (2009-04-07 18:04) [6]


> DiamondShark ©   (07.04.09 17:41) [4]

Вы либо очень-очень крутой, либо загнанный в рамки программист, раз пишете такое.


> Тимохов_   (07.04.09 16:54) [3]

Можно и так, но это сложнее реализовать. Как такового "своего" менеджера памяти у меня нет, у меня есть только указатель на память, где нужно (можно) разместить объект.


> oxffff ©   (07.04.09 10:37) [2]

Как обычно в своем репертуаре :)) Спасибо, возьму на заметку :)


> Медвежонок Пятачок ©   (07.04.09 10:06) [1]

Не совсем понятно о чем вы говорите.


 
oxffff ©   (2009-04-07 18:10) [7]


> Дмитрий С ©   (07.04.09 18:04) [6]


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

Естественно перед вызовом конструктора ему нужно передать dl регистр как 0.

asm
mov eax, <- здесь адрес объекта твоего менеджера.
xor dl,dl;
call Tobject.create;
end;
..............

asm
xor dl,dl;
mov eax,a;
mov ecx,[eax];
call [ecx+vmtOffset Tobject.destroy];
end;


 
Игорь Шевченко ©   (2009-04-07 18:11) [8]

а какая нафиг разница, если и там и там GetMem/FreeMem ?


 
Дмитрий С ©   (2009-04-07 18:33) [9]


> Тимохов_   (07.04.09 17:58) [5]

Тут вопрос даже не оптимизации. Раскрою все карты. Использую компонент VirtualStringTree, который может сам выделять необходимое (ему указывается сколько) количество памяти для *данных* каждой строки, плюс еще память для своих нужд. При этом он вызывает событие инициализации и освобождения для каждой строки. Как я понял данный компонент сам оптимизирует выделение и освобождение памяти так, что, при добавлении сразу нескольких строк, память выделяется однажды сразу на всех. При использовании рекодов инициализация и финализация *данных* осуществляется просто Initialize/Finalize. С классами выходит сложнее. Вот и возникают два варианта: 1) требовать от VirtualStringTree по 4 байта для каждой строки и *данные* использовать как указатель на нормально создаваемый/освобождаемый объект и 2) требовать InstanceSize байт и инициализировать объект в них. Я выбрал второй способ.


> Игорь Шевченко ©   (07.04.09 18:11) [8]

А что будет, если сделать так:
GetMem(Ptr, SizeOf(Ptr^)*2);
Inc(Ptr);
FreeMem(Ptr);


 
Медвежонок Пятачок ©   (2009-04-07 18:49) [10]

> Медвежонок Пятачок ©   (07.04.09 10:06) [1]

Не совсем понятно о чем вы говорите.


Если экземпляры класса едят много памяти, и их много, то почему бы не распределить эту память сбоку, а в классах оставить ссылки на начало "своего" блока и длину?

зачем надо "подсовывать" инстансы в распределенную память?


 
Тимохов_   (2009-04-07 19:02) [11]


> Дмитрий С ©   (07.04.09 18:33) [9]
>
> С классами
> выходит сложнее. Вот и возникают два варианта: 1) требовать
> от VirtualStringTree по 4 байта для каждой строки и *данные*
> использовать как указатель на нормально создаваемый/освобождаемый
> объект и 2) требовать InstanceSize байт и инициализировать
> объект в них. Я выбрал второй способ.


Ну идея ясна. Ну в обощем-то в первоначальном варианте все рабочее вполне. Только я так понял можно сразу Destroy вызывать ну и перекрыть FreeInstance (как у тебя).

Хотя я не могу понять вот чего - ты с рекордами умеешь выполнять initialize/finalize (т.е. есть входа точка, где ты работаешь с память для записей), почему не можешь вызывать Create/Free для объектов?

Я бы сам сделал как с записями - сам *штатно* выделал бы память и *штатно* высвобождал бы ее.


 
Rouse_ ©   (2009-04-07 19:10) [12]


> А что будет, если сделать так:
> GetMem(Ptr, SizeOf(Ptr^)*2);

Будет больно :)


 
Дмитрий С ©   (2009-04-07 19:17) [13]


> Тимохов_   (07.04.09 19:02) [11]

Initialize() работает с уже выделенной памятью, а Create - прежде выделяет ее. Из-за этой разности вопрос, собственно, и возник.

Кстати, при использовании String-ов в объектах, польза от данной оптимизации сводится к нулю, т.к. для них все-равно будет выделена новая память.

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


 
Дмитрий С ©   (2009-04-07 19:22) [14]


> Будет больно :)

Вот и я о том же. Нельзя в данном случае объекту делать FreeMem(Self).

Кстати, у меня только что вопрос возник. Пусть есть var Obj: TObject. После выполнения Obj := TObject.Create будут ли какие-либо данные *про этот объект* по отрицательному смещению относительно Obj?


 
test ©   (2009-04-07 19:31) [15]

А почему бы не использовать существующие классы контейнеры(TObjectList)?


 
Игорь Шевченко ©   (2009-04-07 19:52) [16]


> А что будет, если сделать так:
> GetMem(Ptr, SizeOf(Ptr^)*2);
> Inc(Ptr);
> FreeMem(Ptr);


А зачем так делать и причем тут объекты ?


 
Дмитрий С ©   (2009-04-07 19:56) [17]


> А зачем так делать и причем тут объекты ?

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


 
oxffff ©   (2009-04-07 21:40) [18]


> Дмитрий С ©   (07.04.09 18:33) [9]


Что конкретно у тебя не получается?


 
oxffff ©   (2009-04-07 21:48) [19]


> Дмитрий С ©   (07.04.09 19:22) [14]
>
> > Будет больно :)
>
> Вот и я о том же. Нельзя в данном случае объекту делать
> FreeMem(Self).
>
> Кстати, у меня только что вопрос возник. Пусть есть var
> Obj: TObject. После выполнения Obj := TObject.Create будут
> ли какие-либо данные *про этот объект* по отрицательному
> смещению относительно Obj?


Про этот объект не будет. А служебные поля heap manager никто не запрещает. Но тебя это не должно волновать.

Даже в 2009 где Tobject.instanceSize уже 8.Читать
http://blogs.teamb.com/craigstuntz/2009/03/25/38138/

Разъяснение ниже привожу здесь

Update: In comments, Allen Bauer explains the reasons for this implementation:

The reason for always placing the monitor reference field at the end of the object instance is mainly for backward compatibility. I didn’t want to alter the layout of objects in case there was some code out there that depended upon it. Also, by hiding the implementation the chance of inadvertent mucking with the monitor data was reduced and therefore it increased the overall safety of using it.

If you look closely at what happens on descendant instances, the monitor field is always the last field of the instance. This happens because the compilers (Delphi & C++) ensure that the field is allocated as the last step in the process of laying out the class instance. By doing this, an object laid out in pre-Delphi 2009 will be laid out exactly the same in Delphi 2009 except there is now an extra trailing pointer-sized field.


 
Городской Шаман   (2009-04-08 06:11) [20]


> Дмитрий С ©   (07.04.09 18:04) [6]


Это всё проще делать на уровне менеджера памяти.

Используйте FastMM, скорость при создании кучи мелких объектов возрастёт в 3-6 раз.
http://sourceforge.net/projects/fastmm/


 
SPeller ©   (2009-04-08 07:50) [21]


> При использовании рекодов инициализация и финализация *данных*
> осуществляется просто Initialize/Finalize

Используй object

TMyObject  = object
  ...
end;
PMyObject = ^TMyObject;

Та же запись, только с методами, да еще и с перекрываемыми. SizeOf(TMyObject) = размеру под хранение твоих данных.

PMyObject(SomePtr).DoSmth;


 
KSergey ©   (2009-04-08 09:14) [22]

А точно надо создавать сто тыщ мильёнов объектов? Не, в теории может и красиво каждую точку экрана, например, описать как класс и насоздавать соотв. количество инстансов, вот только теория от практики все ж отличается.


 
Дмитрий С ©   (2009-04-08 10:49) [23]


> А точно надо создавать сто тыщ мильёнов объектов?

Точно не сто тыщ миллионов. От силы 1000 или 5000. Просто в VST начало сабжевого способа реализовано, решил поддержать :) Да и на будущее узнать как поступать, если будет сто тыщь объектов =)


 
atruhin ©   (2009-04-08 17:13) [24]

> [9] Дмитрий С ©   (07.04.09 18:33)
> Как я понял данный компонент сам оптимизирует выделение
> и освобождение памяти так, что, при добавлении сразу нескольких
> строк, память выделяется однажды сразу на всех. При использовании
> рекодов инициализация и финализация *данных* осуществляется
> просто Initialize/Finalize.

Откуда дровишки? Никогда такого небыло. Какая версия VTV? По крайней мере до Version 4.5.5, каждый узел выделялся отдельно,
никаких Finalize VTV не вызывал. Вот фрагмент:
function TBaseVirtualTree.MakeNewNode: PVirtualNode;

var
 Size: Cardinal;

begin
 Size := TreeNodeSize;
 if not (csDesigning in ComponentState) then
 begin
   // Make sure FNodeDataSize is valid.
   if FNodeDataSize = -1 then
     ValidateNodeDataSize(FNodeDataSize);

   // Take record alignment into account.
   Inc(Size, FNodeDataSize);
 end;

 {$ifdef UseLocalMemoryManager}
   Result := FNodeMemoryManager.AllocNode(Size + FTotalInternalDataSize);
 {$else}
   Result := AllocMem(Size + FTotalInternalDataSize);
 {$endif UseLocalMemoryManager}


 
Дмитрий С ©   (2009-04-09 05:33) [25]

Ну вот смотри Result := FNodeMemoryManager.AllocNode(Size + FTotalInternalDataSize); :)


 
atruhin ©   (2009-04-09 16:39) [26]

> [25] Дмитрий С ©   (09.04.09 05:33)
> Ну вот смотри Result := FNodeMemoryManager.AllocNode(Size + FTotalInternalDataSize); :)

Ну и что? Просто используется альтернативный менеджер, со своим списком занятых/свободных блоков.
Но выделяется каждый узел отдельно: Size + FTotalInternalDataSize - размер ОДНОГО узла.
Но не зависимо от этого, тебе нужно распределять твой собственный класс, причем всего один,
так что не нужно никаких извращений с ассемблером и т.д., вполне легитимно перекрываешь
NewInstance и FreeInstance и все.



Страницы: 1 вся ветка

Форум: "Прочее";
Текущий архив: 2009.06.14;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.53 MB
Время: 0.005 c
1-1211343260
Julia
2008-05-21 08:14
2009.06.14
TJvInterpreterProgram


15-1239222048
Summer
2009-04-09 00:20
2009.06.14
Работа с датой и ее перевод в число


15-1238996189
Int23
2009-04-06 09:36
2009.06.14
Проблемы с RAS на Вин2003


15-1239290520
DynaBlaster
2009-04-09 19:22
2009.06.14
Проектирование БД


15-1239285021
12
2009-04-09 17:50
2009.06.14
Поддерживает.. Что за глупое выражение





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский