Утечки памяти 2
Навеяло этим: http://www.fast-report.com/ru/forum/index.php?showtopic=2775
Еще пара мест, замеченных мной.
Первое.
Есть такой конструктор:
и есть такие места в fs_iiparser.pas:
Так вот, в связи с тем, что имя у StringVar пустое, то после второго такого Prog.Add(
объект не записывается в список и висит в памяти до выхода из программы
Предложу такое решение:
Во-первых поставить
FItems.Duplicates := dupError;
в конструкторе
Во-вторых переписать TfsScript.Add так:
Однако тут кроется одна неприятность. Два метода AddEnum и AddEnumSet
могут нарваться на дубликаты в случае добавления, например такого:
в Delphi
Кстати это, если не ошибаюсь, тоже приводит к утечкам памяти если не стоит
FItems.Duplicates := dupError;
или
FItems.Duplicates := dupAccept;(в варианте dupAccept вообще непонятно что будет найдено)
тут нужны более глубокие доработки...
Второе.
Есть такой код в fs_iinterpreter.pas:
никто нигде не освобождает. Я кстати вообще не понял зачем вызывать здесь
NewInstance. Попробовал закомментарить и возвращать 0 - вроде все работает.
Но опасаюсь - может он все-таки нужен?
Если вдруг что-то из этого уже исправлено в новых версиях - pls скажите.
У меня версия 1.5 с доработками. Кстати: меняю ответы на мои вопросы на
технологию повышения скорости выполнения скрипта, например такого:
с 6259 ms до 921 ms
В Delphi, кстати он выполняется за ~10ms (если переменные Variant - для справедливости) то есть всего лишь в 92 раза быстрее, а не в 623
Еще пара мест, замеченных мной.
Первое.
Есть такой конструктор:
constructor TfsScript.Create(AOwner: TComponent);
begin
inherited;
FItems := TStringList.Create;
FItems.Sorted := True;
...
по-умолчанию свойство Duplicates получается dupIgnoreи есть такие места в fs_iiparser.pas:
...
StringVar := TfsStringVariable.Create('', fvtString, '');
StringVar.Value := xi[1].Prop['text'];
Prog.Add(StringVar);
...
конкретно это место используется при присвоении обработчиков процедурным типам в скрипте (... Button1.OnClick:=@MyButton1OnClick; ....)Так вот, в связи с тем, что имя у StringVar пустое, то после второго такого Prog.Add(
объект не записывается в список и висит в памяти до выхода из программы
Предложу такое решение:
Во-первых поставить
FItems.Duplicates := dupError;
в конструкторе
Во-вторых переписать TfsScript.Add так:
procedure TfsScript.Add(const Name: String; Item: TObject);
begin
if Name = '' then
begin
inc(FNoNameCount);
FItems.AddObject('___NoName'+inttostr(FNoNameCount), Item);
else
FItems.AddObject(Name, Item);
if Item is TfsCustomVariable then
TfsCustomVariable(Item).AddedBy := FAddedBy;
end;
FNoNameCount - это integer в Private класса TfsScriptОднако тут кроется одна неприятность. Два метода AddEnum и AddEnumSet
могут нарваться на дубликаты в случае добавления, например такого:
в Delphi
TFontStyle = (fsBold, fsItalic, fsUnderline, fsStrikeOut);
TFontStyles = set of TFontStyle;
в FS:
AddEnum('TFontStyles', 'fsBold, fsItalic, fsUnderline, fsStrikeOut')
AddEnumSet('TFontStyles', 'fsBold, fsItalic, fsUnderline, fsStrikeOut')
в общем понятно - список не даст два раза добавить fsBold и т.п.Кстати это, если не ошибаюсь, тоже приводит к утечкам памяти если не стоит
FItems.Duplicates := dupError;
или
FItems.Duplicates := dupAccept;(в варианте dupAccept вообще непонятно что будет найдено)
тут нужны более глубокие доработки...
Второе.
Есть такой код в fs_iinterpreter.pas:
function TfsClassVariable.GetValue: Variant;
begin
if Params[0].Value = Null then
Result := Integer(FClassRef.NewInstance) else { constructor call }
Result := Params[0].Value; { typecast }
Params[0].Value := Null;
end;
Если я не ошибаюсь, то тот указатель, который вернет FClassRef.NewInstanceникто нигде не освобождает. Я кстати вообще не понял зачем вызывать здесь
NewInstance. Попробовал закомментарить и возвращать 0 - вроде все работает.
Но опасаюсь - может он все-таки нужен?
Если вдруг что-то из этого уже исправлено в новых версиях - pls скажите.
У меня версия 1.5 с доработками. Кстати: меняю ответы на мои вопросы на
технологию повышения скорости выполнения скрипта, например такого:
procedure Button1OnClick(Sender:TObject);
var
i, j: Integer;
l: TList;
tc:variant;
begin
label1.caption:='Running...';
processmessages;
tc:=GetTickCount;
l := TList.Create;
for i := 0 to 400000 do
begin
l.Add(nil);
l.Delete(0);
end;
l.free;
tc:=GetTickCount-tc;
label1.caption:=floattostr(tc)+' ms';
end;
с 6259 ms до 921 ms
В Delphi, кстати он выполняется за ~10ms (если переменные Variant - для справедливости) то есть всего лишь в 92 раза быстрее, а не в 623
Комментарии
...
Result := Integer(FClassRef.NewInstance)
...
немного поторопился.
Некоторые классы без этого не создаются. Будем разбираться...
constructor TfsScript.Create(AOwner: TComponent);
begin
inherited;
FItems := TStringList.Create;
FItems.Sorted := True;
FItems.Duplicates := dupAccept;
2) Чтобы создать экземпляр произвольного класса, надо сделать:
var
cl: TClass;
result: TObject;
cl := TButton;
result := cl.NewInstance;
result.Create(nil);
то же самое делается в FR.
ps. жду технологию
да, а как бороться с перечислениями?
AddEnum('TFontStyles', 'fsBold, fsItalic, fsUnderline, fsStrikeOut')
AddEnumSet('TFontStyles', 'fsBold, fsItalic, fsUnderline, fsStrikeOut')
по одной версии fsItalic это 1 а по другой 2 и обе константы в списке под одним именем
Это понятно, проблема в другом: часть объектов определенных классов, зарегистрированных в скрипте почему-то вызывают утечки памяти, которые отлавливаются MemCheck - ом примерно так:
при этом объект точно должен был "от-Free-тся" так как создавался с Owner -ом который точно уничножился.
Но механизм ошибки я еще не понял. Буду разбираться - может вообще проблема в самих классах.
Насчет скорости.
1. Переписаны классы ....rtti...
Вместо структуры: используется
В общем убираются все if по имени в верхнем регистре на прямые ссылки на функцию
ускорение в моем примере с 6259ms до 4887ms 12%
2.1 Вводится параметр "количество параметров" в метод
2.2 Добавляются два свойства
Соответственно в AddMethod они заполняются
2.3 Переписывается function TfsMethodHelper.GetValue: Variant;
с целью:
1) убрать вызов VarArrayCreate когда нет параметров или параметр один
2) убрать AnsiUpperCase
Когда параметр один он передается непосредственно через переменную v в FOnCall(Instance, FClassRef, s, v)
На самом деле лучше сделать передачу параметров без вариантного массива и при количестве параметров 2 и более, но это требует более кардинальных изменений - я пока это не сделал, времени не было. В принципе большинство функций и методов, особенно часто вызываемых в каких-нибудь циклах как раз имеют до двух параметров (обычно это преобразования, получение поле датасетов и т.п)
Дальше в также убирается
AnsiUpperCase
Ускорение в том же примере с 4887ms до 921 ms
Я еще замерял время с AnsiUpperCase, но без VarArrayCreate: 1312ms
Собственно вот. Разумеется, какая-нибудь математика в скриптах от всего этого не ускоряется. Но у меня в проекте использование этой оптимизации подходит очень хорошо (работа в основном с зарегистрированными в скриптах объектами датасеты, TField, TList ... , часть собственных )
Небольшое замечание по оформлению "оберток" над классами. Почему-то при увеличении количества методов в каждом TFunctions начинает потихоньку падать скорость. Видимо это связано с внутренним поиском адреса в таблице методов класса. Лучше делать так: один класс - одна обертка.
Да, еще остались if ...GET else ....SET в обработчиках для свойств - их наверное тоже можно как-нибудь избавить от if
Да, выгоднее сделать параметры без VarArrayCreate.
Убрать AnsiUpperCase - вообще халява. Кстати можно ли не вводить отдельное поле, а использовать Name ? Я побоялся.
На самом деле у меня сейчас переписаны только fs_isysrtti.pas,fs_idbrtti.pas, часть fs_iclassesrtti и юниты, регистрирующие мои классы. То есть можно выборочно.
Спасибо. Бум переходить со своей самодельщины.
С мелкой утечкой памяти про которую писал разобрался - дело в синтаксисе регистрации конструктора моего класса в скрипте - сам виноват.
Тут еще утечек наловил
При ошибках компиляции скрипта остается много "мусора" в памяти.
К примеру если один раз откомпилить такой код (fs1.9): то получится такое при выходе из программы (подключен FastMM4):
Насколько я понял все это от такого кода, в ассортименте встречающегося в fs_iiparser.pas: теряем result
таких мест много (по крайней мере не одно)
Конечно, ошибки при компиляции - не "основной режим работы" программы, но все-таки нехорошо. Дело в том что я прикрутил к редактору кода своего рода "Code Completion" принцип которого в двух словах: по Ctrl+Space выполняется попытка откомпилить скрипт во временном TfsScript и вне зависимости от результата выполняется поиск в нем сначала TfsProcVariable - текущей процедуры/функции, потом TfsCustomVariable в ней по имени на котором вызван Ctrl+Space. Потом еще поиск в самом скрипте, если не нашли в процедуре/функции. Ну потом понятно, если нашли TfsCustomVariable, то определяем по ней тип и выводим что нужно.
Так вот такие утечки при этом лезут во всю.
И еще, пока не забыл. Было бы, наверное не вредно включить в сл. версию такую доработку:
Сейчас скрипт позволяет писать такой бред: Я предлагаю различать fvtClass и fvtClassReference (ввести такой новый)
Дальше такие изменения:
Вроде ничего не забыл. Теперь при попытке компиляции кода получаем: Incompatible types: 'Class TComponent', 'Class reference TComponent'