Impose a soft memory limit with a Gc.alarm
Example mostly from https://ocaml.org/manual/5.4/api/Gc.html. This repeatedly doubles a string in length, printing its length in a loop, until the alarm notices >100MB memory used and raises an exception.
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 ())
let bloat () =
let rec loop s =
print_int (String.length s);
print_newline ();
loop (s ^ s)
in loop "bloat"
let () = run_with_memory_limit 100 bloat |> ignore
Performing an action on a garbage-collected value
let () =
at_exit Gc.full_major;
let s1 = "not heap-allocated" in
let s2 = "Hello, world" ^ (String.make 1 '!') in
Gc.finalise print_endline s1;
Gc.finalise print_endline s2
Gc.finalise is perhaps supposed to raise Invalid_argument in s1's case, but the manual notes, of 'a Lazy.t,
the compiler sometimes optimizes them in a way that prevents finalise from detecting them
Future optimization may reasonably see through s2's construction, and it's not guaranteed which thread the finaliser will run on, and this example has to force an otherwise avoided major GC cycle just to demonstration the finaliser. So the usual caveats with finalisers in GC apply.
In some other languages with GC, it's surprisingly easy to get the GC to collect a value that's still in scope. For example here's a d forums thread about a program with a value on the GC-scanned stack in a debug build but absent from it in a release build, and otherwise only referenced by GC-unscannable kernel memory. I wasn't able to replicate this as easily in OCaml, but it's still possible, requiring extra work to hold the reference.