假设我在长期运行的ml文件中有这个:
let l = [1;2;3]
let l = [1;2;3;4]
let _ = ...
第一个l = [1;2;3]
会在某个时候被GC?
如果代码如下所示:
let l = [1;2;3]
let l = [1;2;3;4]
let l = [1;2;3]
let _ = ...
有三个l
。第1个被第2个阴影,然后第2个被第3个阴影。
以下情况是否可能,因为GC的时间表未确定?
当达到第3 l
时,GC尚未收集第1个[1;2;3]
,因此重复使用或重新引用相同的内存
在第二次l
之后,GC立即收集了第一个[1;2;3]
,然后第三个l
为[1;2;3]
答案 0 :(得分:4)
不在OCaml顶层中,定义新值l
不会释放先前的l
,其中(据我记得实现)永远存在。它并不重要,因为它是一个常量,只需要与产生它的源代码成比例的空间,就像二进制代码一样。
$ rlwrap ocaml
OCaml version 4.00.1
# let l = [ 1 ] ;;
val l : int list = [1]
# let w = Weak.create 1 ;;
val w : '_a Weak.t = <abstr>
# Weak.set w 0 (Some l) ;;
- : unit = ()
# Gc.full_major () ;;
- : unit = ()
# Weak.check w 0 ;;
- : bool = true
#
此true
means l
仍然存在于记忆中。
# let l = [ 2 ] ;;
val l : int list = [2]
# Weak.check w 0 ;;
- : bool = true
# Gc.full_major () ;;
- : unit = ()
# Weak.check w 0 ;;
- : bool = true
#
它仍然没有,尽管它对于可达的精确定义(不是GC使用的定义)是“不可达的”。
两个编译器都不会释放原始l
:
$ cat t.ml
let l = [ 1 ] ;;
let w = Weak.create 1 ;;
Weak.set w 0 (Some l) ;;
Gc.full_major () ;;
Printf.printf "%B\n" (Weak.check w 0) ;;
let l = [ 2 ] ;;
Printf.printf "%B\n" (Weak.check w 0) ;;
Gc.full_major () ;;
Printf.printf "%B\n" (Weak.check w 0) ;;
$ ocamlc t.ml
$ ./a.out
true
true
true
$ ocamlopt t.ml
$ ./a.out
true
true
true
GC的“可达性”定义的另一个例子比人们可能喜欢的定义更接近:
let g () = Gc.full_major ()
let f () = let l = [ 1 ] in (* do something with l; *) g(); 1
在执行g
时(从f
调用),不再可以访问值l
(对于可到达的精确定义),并且可以进行垃圾回收。它不会是因为它仍然从堆栈中引用。 GC具有可达的粗略概念,只有在f
终止后才能释放它。
答案 1 :(得分:3)
取决于你是否仍然在其他地方有引用 - 如果l是唯一引用,那么是,该引用在第二个赋值时释放,并在适当时获得GC。
列表在ocaml中是不可变的; list literals返回新列表实例。
从编程角度来看,你可以对待你的第三个&#34;列表作为一个全新的列表(即使它包含与第一个元素相同的元素)。
从封闭式实现的角度来看,可能是系统足够聪明,知道从第三个分配的第一个列表中重用内存;但是,我无法立即想到一种可行的有效方式,即使用当前编写的代码。所以,在第三项任务中:
注意
基于@ PascalCuoq的答案,以及一些实验:
如果您将变量定义为顶级变量,则行为会有很大差异。 OCaml垃圾收集器将顶级声明视为(永久?)垃圾收集根 - 因此即使无法再从运行代码中获取它们,它们也不会被GC。
因此,如果上面的例子是在顶级执行的,那么内存中将会有三个不同的&#34; l
&#34;&#34;&#39;&#39;&#39;在内存中,没有一个会被垃圾收集。