如何解决在某些情况下,Ada 会自动释放内存吗?
我试图找到一些关于为什么关键字 new
可用于动态分配对象但没有像 delete
这样的关键字可用于解除分配对象的信息。仔细阅读 Ada 2012 参考手册中对 Ada.Unchecked_Deallocation
的提及,我发现了一些有趣的摘录:
每个对象在被销毁之前都已完成(例如,通过 留下包含 object_declaration 的 subprogram_body,或通过调用 Unchecked_Deallocation)
每个访问对象类型都有一个关联的存储池。分配器分配的存储空间来了 从游泳池; Unchecked_Deallocation 实例将存储返回到池中。
用户定义的存储池对象P的Deallocate过程可能会被实现调用 仅在允许对 P 进行 Allocate 调用的地方为池为 P 的类型 T 解除分配存储, 在 T 的 Unchecked_Deallocation 实例执行期间,或作为最终确定的一部分 T的集合。
如果我不得不猜测,这意味着当执行离开 access 的范围时,实现可以自动释放与 access 关联的对象。 em> 被声明。无需显式调用 Unchecked_Deallocation
。
这似乎得到了 a section in Ada 95 Quality and Style Guide 的支持,它指出:
未经检查的存储解除分配机制是一种覆盖回收已分配存储的默认时间的方法。最早的默认时间是对象不再可访问时,例如,当控制离开声明访问类型的范围时(此时间之后的确切时间点取决于实现)。如果尝试访问对象,则在此之前执行的任何未经检查的存储解除分配都可能导致错误的 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
?
解决方法
如Memory Management with Ada 2012所述,引用here,局部变量通常分配在堆栈上;当变量的作用域退出时,它的内存会自动释放。相比之下,动态变量通常分配在堆上;它的内存是使用new
分配的,它的内存必须被回收,通常:
-
明确地,例如使用
Unchecked_Deallocation
的实例。 -
隐含地,例如使用派生自
Finalization
的受控类型;如前所述 here,当受控实例的范围退出时,自动终结调用Finalize
,它以适合类型设计的方式回收存储。
Ada.Containers
的子代在内部使用受控类型来封装访问值并自动管理内存。作为参考,请将编译器对特定容器的实现与引用 here 的相应功能容器进行比较。
Ada 提供了多种管理内存的方法,在幻灯片 28 中按照作者的偏好顺序进行了总结:
- 基于堆栈。
- 基于容器。
- 基于最终确定。
- 基于子池。
- 手动分配/解除分配。
在 Main
的特殊情况下,程序为 Integer
的 16 个实例分配存储空间。如幻灯片 12 所述,“当相应的访问类型超出范围时,编译器可能回收分配的内存。”例如,最近版本的 GNAT 参考手册表明遵循以下存储管理实施建议:
匿名访问类型的存储池应在该类型的分配器处创建,并在指定对象不可访问时回收。
如果没有这样的指示,则不需要要求回收存储。它通常在程序退出时由主机操作系统回收。
,您的程序会泄漏内存吗?这取决于编译器。
AFAIK,只有两次要求编译器回收分配的内存:
- 当指定了
Storage_Size
的访问类型超出范围时 - 当使用非空值调用
Ada.Unchecked_Deallocation
的实例时
但是,编译器在其他情况下允许回收内存。例如,编译器可能会实现垃圾收集,但我不知道有任何实现。
FWIW,我不知道您的程序不泄漏内存的任何编译器。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。