Ada 在某些情况下会自动释放内存吗?

Does Ada deallocate memory automatically under some circumstances?

我试图找到一些信息,说明为什么关键字 new 可用于动态分配对象,但没有像 delete 这样的关键字可用于释放它们。通过 Ada 2012 参考手册 中提到的 Ada.Unchecked_Deallocation,我发现了一些有趣的摘录:

Every object is finalized before being destroyed (for example, by leaving a subprogram_body containing an object_declaration, or by a call to an instance of Unchecked_Deallocation)

Each access-to-object type has an associated storage pool. The storage allocated by an allocator comes from the pool; instances of Unchecked_Deallocation return storage to the pool.

The Deallocate procedure of a user-defined storage pool object P may be called by the implementation to deallocate storage for a type T whose pool is P only at the places when an Allocate call is allowed for P, during the execution of an instance of Unchecked_Deallocation for T, or as part of the finalization of the collection of T.

如果我不得不猜测,这意味着当执行离开 access 的范围时,实现可以自动解除分配与 access 关联的对象 access 已声明。无需显式调用 Unchecked_Deallocation.

这似乎得到了 a section in Ada 95 Quality and Style Guide 的支持,其中指出:

The unchecked storage deallocation mechanism is one method for overriding the default time at which allocated storage is reclaimed. The earliest default time is when an object is no longer accessible, for example, when control leaves the scope where an access type was declared (the exact point after this time is implementation-dependent). Any unchecked deallocation of storage performed prior to this may result in an erroneous Ada program if an attempt is made to access the object.

但是措辞比较不清楚。如果我要 运行 这段代码,在内存方面究竟会发生什么?

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;

是否保证内存泄漏或 XRun 完成时被释放?

Memory Management with Ada 2012, cited here 中所述,局部变量通常分配在 堆栈 上;当变量的范围退出时,它的内存会自动释放。相比之下,动态变量通常分配在heap上;它的内存是使用new分配的,它的内存必须被回收,通常是:

  • 明确地,例如使用 Unchecked_Deallocation.

    的实例
  • 隐含地,例如使用派生自 Finalization 的受控类型;如前所述 here,当受控实例的范围退出时,自动完成调用 Finalize,以适合类型设计的方式回收存储。

Ada.Containers 的子级在内部使用受控类型来封装访问值并自动管理内存。作为参考,将编译器对特定容器的实现与引用的相应功能容器进行比较 here.

Ada 提供了多种管理内存的方法,在幻灯片 28 中按作者的优先顺序进行了总结:

  1. 基于堆栈。
  2. 基于容器。
  3. 基于定稿。
  4. 基于子池。
  5. 手动 allocate/deallocate.

Main 的特定情况下,程序为 Integer 的 16 个实例分配存储空间。正如幻灯片 12 中所述,“当相应的访问类型超出范围时,编译器 可能 回收分配的内存。”例如,最新版本的 GNAT 参考手册表明遵循以下存储管理实施建议:

A storage pool for an anonymous access type should be created at the point of an allocator for the type, and be reclaimed when the designated object becomes inaccessible.

如果没有这样的指示,则不需要回收存储。它通常在程序退出时由主机操作系统回收。

你的程序会泄漏内存吗?这取决于编译器。

据我所知,只有两次需要编译器来回收分配的内存:

  1. 当指定 Storage_Size 的访问类型超出范围时
  2. 当使用非空值调用 Ada.Unchecked_Deallocation 的实例时

但是,允许编译器在其他情况下回收内存。例如,一个编译器可能会实现垃圾回收,但我不知道有哪个会这样做。

FWIW,我不知道您的程序不会泄漏内存的任何编译器。