Я использую экземпляр пакета Ada.Containers.Formal_Indefinite_Vectors для хранения двух видов полиморфных объектов.
У меня есть следующий пакет, в котором я создаю контейнер:
with Interfaces.C;
with Root.Classes.Concrete_1;
with Root.Classes.Concrete_2;
package Root.Vectors is
type vector_t is tagged limited private;
subtype vectorIndex_t is Interfaces.C.int range 1 .. Interfaces.C.int'Last;
procedure pAppend (this : in out vector_t;
New_Item : Root.Classes.Parent_t'Class);
procedure pClear (this : in out vector_t);
private
--TODO: I have to define it correctly, it could be the problem
function " = " (Left, Right : Root.Classes.Parent_t'Class)
return Boolean is (True);
MaxSize : constant Natural := Natural'Max
(Root.Classes.Concrete_1.Concrete_1_t'Size,
Root.Classes.Concrete_2.Concrete_2_t'Size);
package polimorphicVector_pck is new
Ada.Containers.Formal_Indefinite_Vectors
(Index_Type => vectorIndex_t,
Element_Type => Root.Classes.Parent_t'Class,
" = " => " = ",
Max_Size_In_Storage_Elements => MaxSize,
Bounded => True);
type vector_t is tagged limited
record
v : polimorphicVector_pck.Vector (Capacity => 1000); --TODO: magic number
end record;
end Root.Vectors;
package body Root.Vectors is
procedure pAppend (this : in out vector_t;
New_Item : Root.Classes.Parent_t'Class) is
begin
polimorphicVector_pck.Append (Container => this.v,
New_Item => New_Item);
end pAppend;
procedure pClear (this : in out vector_t) is
begin
polimorphicVector_pck.Clear (Container => this.v);
end pClear;
end Root.Vectors;
Затем я тестирую его со следующим основным:
with Root.Classes.Concrete_1;
with Root.Vectors;
procedure Main is
aVector : Root.Vectors.Vector_t;
begin
for idx in Natural range 1 .. 1000 loop
declare
--Concrete_1_t is an unconstrained tagged type that requires constructor
obj : Root.Classes.Concrete_1.Concrete_1_t :=
Root.Classes.Concrete_1.fConstructor (Argument => idx);
begin
aVector.pAppend (New_Item => obj);
end;
end loop;
-- Trying to clear the vector after all appends; this does not seem to work
aVector.pClear;
end Main;
Затем я использовал gnatmem, чтобы проверить, нет ли у меня утечки памяти, и показал следующее:
Global information
------------------
Total number of allocations :779831
Total number of deallocations :5080
Final Water Mark (non freed mem) : 26.71 Megabytes
High Water Mark : 26.71 Megabytes
Allocation Root # 1
-------------------
Number of non freed allocations :764550
Final Water Mark (non freed mem) : 17.50 Megabytes
High Water Mark : 17.50 Megabytes
Backtrace :
??:0 ??
Allocation Root # 2
-------------------
Number of non freed allocations :5100
Final Water Mark (non freed mem) : 119.53 Kilobytes
High Water Mark : 119.53 Kilobytes
Backtrace :
a-cfinve.adb:220 root.vectors.polimorphicVector_pck.copy
Allocation Root # 3
-------------------
Number of non freed allocations :3390
Final Water Mark (non freed mem) : 7.78 Megabytes
High Water Mark : 7.78 Megabytes
Backtrace :
a-cfinve.adb:466 root.vectors.polimorphicVector_pck.find_index
Allocation Root # 4
-------------------
Number of non freed allocations :1710
Final Water Mark (non freed mem) : 1.32 Megabytes
High Water Mark : 1.32 Megabytes
Backtrace :
a-cfinve.adb:219 root.vectors.polimorphicVector_pck.copy
Allocation Root # 5
-------------------
Number of non freed allocations : 1
Final Water Mark (non freed mem) : 8 Bytes
High Water Mark : 8 Bytes
Backtrace :
??:0 system.stream_attributes.xdr.i_ssi
Почему течет? Это может быть связано с тем, что «=» всегда возвращает True?
Придирка к вашему коду: ваш Max_Size — это максимальный размер в битах (из атрибута «Размер»), но для создания экземпляра Formal_Indefinite_Vectors требуется максимальный размер в элементах хранения (обычно в байтах). Таким образом, вы выделяете как минимум в восемь раз больше памяти, чем требуется.
Интересно, какой выпуск компилятора вы используете? gnatmem.adb был удален из исходников FSF в 2014 году.
Утечки явно есть; без минимального воспроизводимого примера (в частности, Roots.Classes
иерархии) трудно сказать больше
@SimonWright Я нигде не использую динамическое размещение; конкретные классы имеют дискретные члены, максимальный размер которых известен.
Если бы вы показали нам код в иерархии Root.Classes
, мы могли бы помочь вам больше. Кроме того, какой выпуск компилятора вы используете? и на какой платформе?
Вы создаете контейнер как Bounded:
Bounded => True);
Ограниченные контейнеры размещаются в стеке. Это также задокументировано в spec-файле:
Bounded : Boolean := True;
-- If True, the containers are bounded; the initial capacity is the maximum
-- size, and heap allocation will be avoided. If False, the containers can
-- grow via heap allocation.
Спасибо за ваш ответ! Итак, у меня нет утечек памяти, эти выделения связаны с объявлением переменной «aVector»?
@ Альбатрос23, да
ой, вопрос новичка xD Большое спасибо
Да, Bounded => True должен заставить Formal_Indefinite_Vectors использовать только выделение стека, основываясь на комментариях (и я также бегло просмотрел код). Однако, хотя я никогда не использовал gnatmem, если бы мое первое использование gnatmem дало результаты, которые вы показываете, я бы, конечно, заподозрил, что есть утечки памяти. Я предлагаю вам поэкспериментировать с изменением верхней границы цикла for, например, протестировав 1 .. 0, 1 .. 1, 1 .. 2 и 1 .. 10, и посмотреть, как количество распределений и освобождений сообщаемые gnatmem, зависят от количества операций добавления.
Я попытался сделать простую демонстрацию для использования с моим WIP-эквивалентом macOS gnatmem
, проблем не возникло. Тем не менее, ваши корни распределения 2, 3 и 4 кажутся очень подозрительными - судя по исходному коду GCC 12.2.0, похоже, что компилятор выделяет память для реализации оператора return!
@SimonWright, как это возможно? У меня есть много переключателей в файле gpr, потому что проект взаимодействует с кодом C++, возможно, это вызвано одним из них. В первый раз у меня был переключатель "-gnata", поэтому весь призрачный код из Ada.Containers.Formal_Indefinite_Vectors выполнялся и замедлял каждое добавление.
Помимо совета Никласа поэкспериментировать с разными числами, чтобы увидеть влияние, я бы также попробовал использовать какой-нибудь тривиальный тип элемента (например, целое число), чтобы исключить любое влияние с этой стороны. Также я бы попытался увидеть, что valgrind
должен сказать.
Я правильно реализовал функцию равенства, но результат тот же