在某些情况下,Ada会自动释放内存吗?
我试图找到一些关于为什么new可以使用关键字来动态分配对象的信息,但是没有像delete这样的关键字可以用来释放它们。经历的提到Ada.Unchecked_Deallocation在阿达2012参考手册,我发现了一些有趣的摘录:
每个对象在被销毁之前都已完成(例如,通过留下包含 object_declaration 的 subprogram_body,或通过调用 Unchecked_Deallocation 的实例)
每个访问对象类型都有一个关联的存储池。分配器分配的存储来自池;Unchecked_Deallocation 的实例将存储返回到池中。
在 Unchecked_Deallocation 实例的执行期间,用户定义的存储池对象 P 的解除分配过程可以被实现调用,以仅在允许对 P 进行分配调用的地方为池为 P 的类型 T 解除分配存储对于 T,或作为 T 集合最终确定的一部分。
如果我不得不猜测,这意味着当执行离开声明访问的范围时,实现有可能自动释放与访问关联的对象。无需显式调用.Unchecked_Deallocation
这似乎得到了Ada 95 质量和样式指南中的一个部分的支持,该部分指出:
未经检查的存储解除分配机制是一种覆盖回收已分配存储的默认时间的方法。最早的默认时间是对象不再可访问时,例如,当控制离开声明访问类型的范围时(此时间之后的确切时间点取决于实现)。如果尝试访问对象,则在此之前执行的任何未经检查的存储解除分配都可能导致错误的 Ada 程序。
但措辞相当不明确。如果我运行这段代码,内存方面到底会发生什么?
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
procedure Run is
X : access Integer := new Integer'(64);
begin
Put (Integer'Image (X.all));
end Run;
begin
for I in 1 .. 16 loop
Run;
end loop;
end Main;
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
procedure Outer is
type Integer_Access is not null access Integer;
procedure Run is
Y : Integer_Access := new Integer'(64);
begin
Put (Integer'Image (Y.all));
end Run;
begin
for I in 1 .. 16 loop
Run;
end loop;
end Outer;
begin
Outer;
end Main;
是否有保证的内存泄漏或X在Run完成时释放?
回答
正如此处引用的Ada 内存管理 2012 中所述,局部变量通常分配在堆栈上;当变量的作用域退出时,它的内存会自动释放。相比之下,动态变量通常分配在堆上;它的内存是使用 分配的,它的内存必须被回收,通常:new
-
明确地,例如使用
Unchecked_Deallocation. -
隐式地,例如使用派生自的受控类型
Finalization;如所指出的在这里,当一个受控实例退出的范围内,自动终结呼叫Finalize,其回收在存储适合于该类型的设计的方式。
子代在Ada.Containers内部使用受控类型来封装访问值并自动管理内存。作为参考,请将编译器对特定容器的实现与此处引用的相应功能容器进行比较。
Ada 提供了多种管理内存的方法,在幻灯片28中按照作者的偏好顺序进行了总结:
- 基于堆栈。
- 基于容器。
- 基于定稿。
- 基于子池。
- 手动分配/解除分配。
在 的特殊情况下Main,程序为 的 16 个实例分配存储空间Integer。如幻灯片12 所述,“当相应的访问类型超出范围时,编译器可以回收分配的内存。” 例如,最近版本的 GNAT 参考手册表明遵循以下存储管理实施建议:
匿名访问类型的存储池应在该类型的分配器点创建,并在指定对象不可访问时回收。
如果没有这样的指示,则不需要回收存储。这是通常由主机操作系统在程序退出时收回。