模块 Stdlib.Gc

module Gc: Gc

type stat = {
   minor_words : float; (*

程序启动以来,在次要堆中分配的字数。

*)
   promoted_words : float; (*

程序启动以来,在次要堆中分配并在次要回收后存活并移动到主要堆的字数。

*)
   major_words : float; (*

程序启动以来,在主要堆中分配的字数,包括已提升的字数。

*)
   minor_collections : int; (*

程序启动以来,次要回收的次数。

*)
   major_collections : int; (*

程序启动以来,主要回收周期完成的次数。

*)
   heap_words : int; (*

主要堆的总大小(以字为单位)。

*)
   heap_chunks : int; (*

构成主要堆的连续内存块的数量。此指标在 OCaml 5 中当前不可用:字段值始终为 0

*)
   live_words : int; (*

主要堆中活动数据的字数,包括头字。

请注意,“活动”字是指主要堆中当前未知为可回收的每个字,其中包括在先前 gc 周期开始后程序变得无法访问的字。通常,调用 Gc.full_major(或 Gc.compact) 然后计算 gc 统计信息会更简单且更可预测,因为那时“活动”字具有“程序可访问”的简单含义。一个需要注意的是,对 Gc.full_major 的单次调用不会回收来自 Gc.finalise 的终结器具有的值(这并不适用于 Gc.finalise_last)。如果此注意事项很重要,只需调用两次 Gc.full_major 而不是一次。

*)
   live_blocks : int; (*

主要堆中活动块的数量。

有关“活动”的含义,请参阅 live_words

*)
   free_words : int; (*

空闲列表中的字数。

*)
   free_blocks : int; (*

空闲列表中块的数量。此指标在 OCaml 5 中当前不可用:字段值始终为 0

*)
   largest_free : int; (*

空闲列表中最大块的大小(以字为单位)。此指标在 OCaml 5 中当前不可用:字段值始终为 0

*)
   fragments : int; (*

由于碎片导致的浪费的字数。这些是放置在两个活动块之间的 1 字节空闲块。它们不可用于分配。

*)
   compactions : int; (*

程序启动以来堆压缩的次数。

*)
   top_heap_words : int; (*

主要堆达到的最大大小(以字为单位)。

*)
   stack_size : int; (*

堆栈的当前大小(以字为单位)。此指标在 OCaml 5 中当前不可用:字段值始终为 0

  • 3.12
*)
   forced_major_collections : int; (*

程序启动以来强制执行的主要回收周期完成的次数。

  • 4.12
*)
}

内存管理计数器以 stat 记录返回。这些计数器提供整个程序的值。

程序启动以来分配的总内存量(以字为单位)为 minor_words + major_words - promoted_words。乘以字大小(32 位机器上为 4,64 位机器上为 8)即可获得字节数。

type control = {
   minor_heap_size : int; (*

次要堆的大小(以字为单位)。更改此参数将触发次要回收。此程序使用的次要堆的总大小是活动域的堆大小的总和。默认值:256k。

*)
   major_heap_increment : int; (*

增加主要堆时要添加的大小。如果此数字小于或等于 1000,则它是当前堆大小的百分比(即,将其设置为 100 将在每次增加时使堆大小加倍)。如果它大于 1000,则它是将添加到堆中的固定字数。默认值:15。

*)
   space_overhead : int; (*

主要 GC 速度从此参数计算。这是由于 GC 不会立即收集无法访问的块而“浪费”的内存。它表示为活动数据使用内存的百分比。如果 space_overhead 较小,则 GC 将工作更多(使用更多 CPU 时间并更积极地收集块)。默认值:120。

*)
   verbose : int; (*

此值控制标准错误输出上的 GC 消息。它是以下一些标志的总和,用于在相应事件上打印消息

  • 0x001 主要 GC 周期的开始和结束。
  • 0x002 次要回收和主要 GC 切片。
  • 0x004 堆的增长和缩小。
  • 0x008 堆栈和内存管理器表的调整大小。
  • 0x010 堆压缩。
  • 0x020 GC 参数的更改。
  • 0x040 主要 GC 切片大小的计算。
  • 0x080 终结器函数的调用。
  • 0x100 启动时字节码可执行文件和共享库搜索。
  • 0x200 压缩触发条件的计算。
  • 0x400 在程序退出时输出 GC 统计信息。默认值:0。
*)
   max_overhead : int; (*

当估计的“浪费”内存量超过活动数据量的 max_overhead 百分比时,将触发堆压缩。如果 max_overhead 设置为 0,则将在每个主要 GC 周期结束时触发堆压缩(此设置仅用于测试目的)。如果 max_overhead >= 1000000,则永远不会触发压缩。默认值:500。

*)
   stack_limit : int; (*

光纤堆栈的最大大小(以字为单位)。默认值:1024k。

*)
   allocation_policy : int; (*

用于在主要堆中分配的策略。

此选项在 OCaml 5.x 中被忽略。

在 OCaml 5.0 之前,可能的值为 0、1 和 2。

  • 0 是 next-fit 策略
  • 1 是 first-fit 策略(自 OCaml 3.11 起)
  • 2 是 best-fit 策略(自 OCaml 4.10 起)
  • 3.11
*)
   window_size : int; (*

主要 GC 用于平滑其工作负载变化的窗口的大小。这是一个介于 1 和 50 之间的整数。默认值:1。

  • 4.03
*)
   custom_major_ratio : int; (*

位于主要堆中的自定义值持有的堆外内存的浮动垃圾与主要堆大小的目标比率。调整 GC 速度以尝试为此类死值使用这么多内存,而这些死值尚未被回收。表示为主要堆大小的百分比。默认值使堆外浮动垃圾的大小与堆内开销大致相同。注意:这仅适用于使用 caml_alloc_custom_mem(例如 bigarrays)分配的值。默认值:44。

  • 4.08
*)
   custom_minor_ratio : int; (*

次要堆中自定义值持有的堆外内存的浮动垃圾的界限。当次要堆中自定义值持有这么多内存时,将触发次要 GC。表示为次要堆大小的百分比。注意:这仅适用于使用 caml_alloc_custom_mem(例如 bigarrays)分配的值。默认值:100。

  • 4.08
*)
   custom_minor_max_size : int; (*

次要堆中每个自定义值分配的堆外内存的最大量。持有超过此字节数的自定义值将在主要堆上分配。注意:这仅适用于使用 caml_alloc_custom_mem(例如 bigarrays)分配的值。默认值:70000 字节。

  • 4.08
*)
}

GC 参数以 control 记录给出。请注意,这些参数也可以通过设置 OCAMLRUNPARAM 环境变量来初始化。请参阅 ocamlrun 的文档。

val stat : unit -> stat

以表示程序总内存统计信息的 stat 记录返回内存管理计数器的当前值。此函数会导致完全主要回收。

val quick_stat : unit -> stat

stat 相同,除了 live_wordslive_blocksfree_wordsfree_blockslargest_freefragments 设置为 0。由于每个域的缓冲区,它可能仅表示自上次次要回收或主要周期以来程序总内存使用的状态。此函数比 stat 快得多,因为它不需要触发完全主要回收。

val counters : unit -> float * float * float

返回当前域或可能的前一个域的 (minor_words, promoted_words, major_words)。此函数与 quick_stat 一样快。

val minor_words : unit -> float

此域或可能的前一个域在次要堆中分配的字数。此数字在字节码程序中是准确的,但在编译为本地代码的程序中仅是近似值。

在本地代码中,此函数不会分配。

val get : unit -> control

control 记录返回 GC 参数的当前值。

val set : control -> unit

set r 根据 control 记录 r 更改 GC 参数。正常用法是:Gc.set { (Gc.get()) with Gc.verbose = 0x00d }

val minor : unit -> unit

触发次要回收。

val major_slice : int -> int

major_slice n 执行一次次要回收和一次主要回收的切片。 n 是切片的大小:GC 将执行足够的工作来释放(平均)n 个字的内存。如果 n = 0,GC 将尝试执行足够的工作以确保下一次自动切片没有工作要做。此函数返回一个未指定的整数(当前:0)。

val major : unit -> unit

执行一次次要回收并完成当前的主要回收周期。

val full_major : unit -> unit

执行一次次要回收,完成当前的主要回收周期,并执行一个完整的新的周期。这将收集所有当前不可到达的块。

val compact : unit -> unit

执行一次完全的主要回收并压缩堆。请注意,堆压缩是一个耗时的操作。

val print_stat : out_channel -> unit

将程序总体的内存管理计数器(以人类可读的形式)的当前值打印到通道参数中。

val allocated_bytes : unit -> float

返回此域和可能的前一个域分配的字节数。它作为 float 返回,以避免在 32 位机器上使用 int 时的溢出问题。

val get_minor_free : unit -> int

返回此域的次要堆内可用空间的当前大小。

val finalise : ('a -> unit) -> 'a -> unit

finalise f vf 注册为 v 的终结函数。 v 必须是堆分配的。在 v 第一次变得不可到达(包括通过弱指针)和 v 被 GC 收集之间的一些时间点,将使用 v 作为参数调用 f。可以为相同的值注册多个函数,甚至可以注册相同函数的多个实例。每个实例将被调用一次(或从不调用,如果程序在 v 变得不可到达之前终止)。

GC 将按照释放的顺序调用终结函数。当多个值同时变得不可到达时(即在同一 GC 周期内),终结函数将按照对应 finalise 调用的反向顺序调用。如果 finalise 的调用顺序与值的分配顺序相同,则意味着每个值在其依赖的值之前完成终结。当然,如果通过赋值引入了额外的依赖关系,则此情况将不再成立。

在存在多个 OCaml 线程的情况下,应假定任何特定的终结器都可能在任何线程中执行。

从终结函数的闭包可到达的任何内容都被认为是可到达的,因此以下代码将无法按预期工作

  •  let v = ... in Gc.finalise (fun _ -> ...v...) v 

相反,您应该确保 v 不在终结函数的闭包中,方法是编写

  •  let f = fun x -> ...  let v = ... in Gc.finalise f v 

f 函数可以使用 OCaml 的所有功能,包括使值再次可到达的赋值。它还可以无限循环(在这种情况下,除非它调用 finalise_release,否则在 f 执行期间不会调用其他终结函数)。它可以在 v 或其他值上调用 finalise 以注册其他函数,甚至注册自身。它可以引发异常;在这种情况下,异常将中断程序在调用该函数时正在执行的操作。

finalise 将引发 Invalid_argument,如果 v 未保证为堆分配的。一些非堆分配的值的示例包括整数、常量构造函数、布尔值、空数组、空列表、单位值。堆分配或非堆分配的确切列表取决于实现。某些常量值可以是堆分配的,但在程序的生命周期内永远不会被释放,例如整数常量列表;这也取决于实现。请注意,类型 float 的值有时被分配,有时不被分配,因此终结它们是不安全的,并且 finalise 也会为它们引发 Invalid_argument。类型 'Lazy.t(对于任何 'a)在这方面类似于 float,除了编译器有时会以一种阻止 finalise 检测它们的方式对其进行优化。在这种情况下,它不会引发 Invalid_argument,但您仍然应该避免在惰性值上调用 finalise

调用 String.makeBytes.makeBytes.createArray.makeref 的结果保证为堆分配的且非常量,除非长度参数为 0

val finalise_last : (unit -> unit) -> 'a -> unit

Gc.finalise 相同,只是值没有作为参数给出。因此,您无法使用给定的值来计算终结函数。好处是该函数在值最后一次不可到达而不是第一次不可到达后被调用。因此,与 Gc.finalise 相反,该值将永远不会再次可到达或再次使用。特别是,在运行终结函数之前,包含此值作为键或数据的每个弱指针和短暂数据都将被取消设置。此外,附加到 Gc.finalise 的终结函数始终在附加到 Gc.finalise_last 的终结函数之前调用。

val finalise_release : unit -> unit

终结函数可以调用 finalise_release 来告诉 GC 它可以在启动下一个终结函数时不必等待当前函数返回。

type alarm 

警报是一段数据,它在主要 GC 周期结束时调用用户函数。以下函数用于创建和删除警报。

val create_alarm : (unit -> unit) -> alarm

create_alarm f 将安排在主要 GC 周期结束时调用 f,而不是由 f 本身引起的,从当前周期或下一个周期开始。 f 将在创建警报的同一域上运行,直到域退出或调用 delete_alarm。将返回一个类型为 alarm 的值,您可以使用它来调用 delete_alarm

不能保证 Gc 警报在每个主要 GC 周期结束时都运行,但保证它最终会运行。

例如,以下是一种粗略的方法,如果程序的内存消耗超过给定的 limit(以 MB 为单位),则中断函数,适用于在顶级环境中使用

let run_with_memory_limit (limit : int) (f : unit -> 'a) : 'a =
  let limit_memory () =
    let mem = Gc.(quick_stat ()).heap_words in
    if mem / (1024 * 1024) > limit / (Sys.word_size / 8) then
      raise Out_of_memory
  in
  let alarm = Gc.create_alarm limit_memory in
  Fun.protect f ~finally:(fun () -> Gc.delete_alarm alarm ; Gc.compact ())
   
val delete_alarm : alarm -> unit

delete_alarm a 将停止对与 a 关联的函数的调用。再次调用 delete_alarm a 没有效果。

val eventlog_pause : unit -> unit
已弃用。 请改用 Runtime_events.pause。
val eventlog_resume : unit -> unit
已弃用。 请改用 Runtime_events.resume。
module Memprof: sig .. end

Memprof 是一个分析引擎,它随机采样已分配的内存字。