我该怎么办才能让GC释放OCaml中未使用的内存?

Jac*_*ale 6 ocaml

我正在为mergesort和quicksort做基准测试.

我实施了Random_list.create,Mergesort.sort_listQuicksort.sort_list.我们可以假设这三个函数都已正确实现,并且在这个问题中实现并不重要.

我想问的是关于OCaml的GC.


这是我的基准代码:

let _ = 
  let l = Random_list.create 10000000 in

  let len1 = List.length (Mergesort.sort_list l) in
  Printf.printf "mergesort done for %d elements" len1;

  let len2 = List.length (Quicksort.sort_list l) in
  Printf.printf "quicksort done for %d elements" len2
Run Code Online (Sandbox Code Playgroud)

如果我运行上面的代码,它告诉我Fatal error: exception Out_of_memory之后mergesort done for 10000000 elements.

它是内存不足,没问题.输出也告诉我Out_of_memory在成功后发生了mergesort.


然后我做的是拆分代码并单独测试:

let _ = 
      let l = Random_list.create 10000000 in
      let len1 = List.length (Mergesort.sort_list l) in
      Printf.printf "mergesort done for %d elements" len1
Run Code Online (Sandbox Code Playgroud)

然后

let _ = 
      let l = Random_list.create 10000000 in
      let len2 = List.length (Quicksort.sort_list l) in
      Printf.printf "quicksort done for %d elements" len2
Run Code Online (Sandbox Code Playgroud)

两者都没有 运行Out_of_memory.


这是我的问题:

从我的基准代码,是的,我做了串行排序:mergesort然后快速排序.

在执行期间,应该创建3个主要列表:l来自mergesort的列表和来自quicksort的列表.

但是,从mergesort创建的列表应该GCed在quicksort 之前,对吧?那个清单没有任何参考,对吧?

在quicksort之前,原来只有一个主要列表,对l吧?

为什么它仍然会Out_of_memory出错?

Chr*_*Lec 2

我认为问题在于您使用的列表非常大。垃圾收集器保留两个不同的堆来管理内存:

  • 用于存放微小/短命对象的小堆。
  • 持久对象的主要堆

小堆会定期清除,如果对象存活的时间足够长,则会将其提升到主堆。

然而,真正大的对象会直接进入主堆。问题是主堆需要停止世界,即停止应用程序。因此,主堆收集是分几个步骤完成的,以确保应用程序不会长时间停止,并且不像次要堆收集那样频繁地进行。

也许在你的情况下,当你开始快速排序时,merge_sort列表仍然没有被收集,因此所有3个列表同时存在于内存中。

您可以要求 GC 进行完整的主要收集,看看是否可以解决问题:

let _ = 
  let l = Random_list.create 10000000 in

  let len1 = List.length (Mergesort.sort_list l) in
  Printf.printf "mergesort done for %d elements" len1;

  Gc.full_major ();

  let len2 = List.length (Quicksort.sort_list l) in
  Printf.printf "quicksort done for %d elements" len2
Run Code Online (Sandbox Code Playgroud)

  • 如果这有帮助的话我会感到非常惊讶。通常,分配器在内存不足时会强制执行 GC,因此不需要显式触发它。(事实上​​,在 99.9% 的情况下,这是一个非常糟糕的主意。) (2认同)
  • “问题是,主要的堆需要停止世界,即停止应用程序。” 具有误导性,即使下面的句子试图澄清这一点。次要 GC 所造成的影响并不比主要 GC 少。事实上,“停止世界”对于增量主要 GC 来说是一个糟糕的选择,但可以说适用于非增量次要 GC。正如 Andreas 已经评论的那样,如果没有可用内存,则主 GC 应该加快速度 - 它会调整其速度,以便在它估计可用内存池将耗尽时准确地完成一个循环 (2认同)