模块 Gc

module Gc: sig .. end

内存管理控制和统计;终结值。


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 统计信息之前,调用 Gc.full_major(或 Gc.compact)要简单且更可预测,因为这样,“活动”字就具有“程序可访问”的简单含义。一个需要注意的是,对 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 是下一次拟合策略
  • 1 是首次拟合策略(自 OCaml 3.11 以来)
  • 2 是最佳拟合策略(自 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

返回 GC 参数的当前值,这些值以 control 记录的形式给出。

val set : control -> unit

set r 更改 GC 参数,以反映 control 记录 r。正常用法是: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 必须是堆分配的。 f 将在 v 第一次变得不可达(包括通过弱指针)和 v 被 GC 收集的时间之间,以 v 作为参数调用。 可以为同一个值注册多个函数,甚至可以注册同一个函数的多个实例。 每个实例都会被调用一次(或者从不调用,如果程序在 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 的所有功能,包括使值再次可达的分配。 它也可以永远循环(在这种情况下,其他终结函数不会在执行 f 期间被调用,除非它调用 finalise_release)。 它可以对 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 将安排 f 在主要 GC 周期结束时被调用,而不是由 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 是一个分析引擎,它随机对分配的内存字进行采样。