You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
zapashcanon 29076561cc
backup
2 months ago
..
README.md backup 2 months ago
compile.sh stuff 4 months ago
dune add readme target 4 months ago
hell.py backup 2 months ago
malloc.c stuff 4 months ago
memory_layout.c first commit 4 months ago
pat.py backup 2 months ago
sbrk.c stuff 4 months ago
unsafe.ml stuff 3 months ago

README.md

Garbage

Allocation mémoire

Allocation statique

On peut allouer de la mémoire statiquement à deux endroits :

  • dans le segment .data lors d'une initialisation explicite,
  • dans le segment .bss lors d'une initialisation par zéro.

On utilise un script pour compiler avec plusieurs options :

#!/bin/sh

# compile.sh

set -eu

name=$(basename "$1" ".c")

gcc -std=c11 -W -Wall -Wextra -Wdouble-promotion -Wformat -Winit-self  -Wmissing-include-dirs -Wswitch-default  -Wswitch-enum -Wunused -Wunused-parameter -Wuninitialized -Wunknown-pragmas -fstrict-overflow -Wstrict-overflow=2 -Wmissing-format-attribute -Wstrict-aliasing -Wfloat-equal -Wundef -Wpointer-arith -Wcast-qual -Wcast-align -Wconversion -Wmissing-declarations -Wredundant-decls -Wpacked -Wpadded -Winline -Wvla -Wdisabled-optimization -Wstack-protector -Winvalid-pch -Wshadow -pedantic-errors --pedantic -Werror -pipe -funsigned-char -fsigned-bitfields -fno-pie -no-pie -O3 ${name}.c -o ${name}.exe
// memory_layout.c

#include <stdio.h>
#include <stdlib.h>

const int x = 3;

int y = 5;

static int i;

char *my_str = "this should appear in .data";

int main(void) {

  printf("x = %d\n", x);

  printf("y = %d\n", y);
  y = 10;
  printf("y = %d\n", y);

  printf("i = %d\n", i);
  i = 42;
  printf("i = %d\n", i);

  exit(0);
}
$ ./compile.sh memory_layout.c
$ size ./memory_layout.exe
   text	   data	    bss	    dec	    hex	filename
   1395	    564	     12	   1971	    7b3	./memory_layout.exe

Allocation dynamique

Avec sbrk

// sbrk.c

#define _DEFAULT_SOURCE // needed for sbrk

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

char tmp[64];
void *current_address;
void *old_address;

int main(void) {

  current_address = sbrk(0);
  fputs("current_address = ", stdout);
  snprintf(tmp, sizeof(tmp), "%p", current_address);
  puts(tmp);

  fputs("calling sbrk(2 * (int)sizeof(int))\n", stdout);
  old_address = sbrk(2 * (int)sizeof(int));
  fputs("old_address = ", stdout);
  snprintf(tmp, sizeof(tmp), "%p", old_address);
  puts(tmp);

  current_address = sbrk(0);
  printf("current address = ");
  snprintf(tmp, sizeof(tmp), "%p", current_address);
  puts(tmp);

  int *x = (int *)old_address;
  int *y = (int *)old_address + 1;

  printf("addr(x) = %p\n", (void *)x);
  printf("addr(y) = %p\n", (void *)y);

  printf("val(x) = %d\n", *x);
  printf("val(y) = %d\n", *y);

  *x = 42;
  *y = 1685;

  puts("setting x to 42");
  puts("setting y to 1685");

  printf("val(x) = %d\n", *x);
  printf("val(y) = %d\n", *y);

  current_address = sbrk(0);
  printf("current address = ");
  snprintf(tmp, sizeof(tmp), "%p", current_address);
  puts(tmp);

  fputs("calling sbrk(-1 * (int)sizeof(int))\n", stdout);
  old_address = sbrk(-1 * (int)sizeof(int));
  fputs("old_address = ", stdout);
  snprintf(tmp, sizeof(tmp), "%p", old_address);
  puts(tmp);

  current_address = sbrk(0);
  printf("current address = ");
  snprintf(tmp, sizeof(tmp), "%p", current_address);
  puts(tmp);

  exit(0);
}
$ ./compile.sh sbrk.c
$ ./sbrk.exe # do `echo 0 | sudo tee /proc/sys/kernel/randomize_va_space` before to be deterministic
current_address = 0x405000
calling sbrk(2 * (int)sizeof(int))
old_address = 0x426000
current address = 0x426008
addr(x) = 0x426000
addr(y) = 0x426004
val(x) = 0
val(y) = 0
setting x to 42
setting y to 1685
val(x) = 42
val(y) = 1685
current address = 0x426008
calling sbrk(-1 * (int)sizeof(int))
old_address = 0x426008
current address = 0x426004

Avec malloc

// sbrk.c

#define _DEFAULT_SOURCE // needed for sbrk

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

char tmp[64];
void *current_address;
void *old_address;

int main(void) {

  current_address = sbrk(0);
  fputs("current_address = ", stdout);
  snprintf(tmp, sizeof(tmp), "%p", current_address);
  puts(tmp);

  puts("allocating x");

  int *x = malloc(1 * sizeof(int));

  current_address = sbrk(0);
  fputs("current_address = ", stdout);
  snprintf(tmp, sizeof(tmp), "%p", current_address);
  puts(tmp);

  puts("allocating y");

  int *y = malloc(1 * sizeof(int));

  current_address = sbrk(0);
  fputs("current_address = ", stdout);
  snprintf(tmp, sizeof(tmp), "%p", current_address);
  puts(tmp);

  printf("addr(x) = %p\n", (void *)x);
  printf("addr(y) = %p\n", (void *)y);

  printf("val(x) = %d\n", *x);
  printf("val(y) = %d\n", *y);

  *x = 42;
  *y = 1685;

  puts("setting x to 42");
  puts("setting y to 1685");

  printf("val(x) = %d\n", *x);
  printf("val(y) = %d\n", *y);

  puts("freeing y");

  free(y);

  current_address = sbrk(0);
  printf("current address = ");
  snprintf(tmp, sizeof(tmp), "%p", current_address);
  puts(tmp);

  puts("freeing x");

  free(x);

  current_address = sbrk(0);
  printf("current address = ");
  snprintf(tmp, sizeof(tmp), "%p", current_address);
  puts(tmp);

  exit(0);
}
$ ./compile.sh malloc.c
$ ./malloc.exe # do `echo 0 | sudo tee /proc/sys/kernel/randomize_va_space` before to be deterministic
current_address = 0x405000
allocating x
current_address = 0x426000
allocating y
current_address = 0x426000
addr(x) = 0x4062b0
addr(y) = 0x4062d0
val(x) = 0
val(y) = 0
setting x to 42
setting y to 1685
val(x) = 42
val(y) = 1685
freeing y
current address = 0x426000
freeing x
current address = 0x426000

Comment se passer de free ? Un glaneur de cellules.

Les différents GC

Le but du GC est de libérer automatiquement la mémoire. Pour cela, il doit savoir quand un bout mémoire ne sera plus utilisé. S'il libère de la mémoire qui est encore accessible, il va créer des dangling pointers, qui s'il est utilisé provoquera une erreur - c'est l'équivalent des use-after-free avec malloc.

Il y a deux façons de procéder. Soit on stocke dans chaque objet le nombre de références vers cette objet et on incrémente/décrémente cette référence à chaque fois qu'une référence est créée ou détruite, quand on atteint zéro, on peut libérer la mémoire utilisée par cette valeur et décrémenter les valeurs vers lesquelles elle pointait : c'est le reference counting. Soit on scanne régulièrement l'ensemble des valeurs accessibles (les racines) comme la pile, les variables globales etc. et en suivant les pointeurs, on regarde l'ensemble des valeurs accessibles, le reste peut être libéré : ce sont les tracing GC.

Le principal problème du reference counting est la gestion des cycles : en effet, s'il est implémenté naïvement, les cycles ne sont jamais libérés et on a des fuites mémoires. Le principal problème des GC tracing est le temps de pause important : on doit interrompre le programme régulièrement et le GC doit parcourir toutes les racines et les objets vers lesquelles elles pointent pour pouvoir libérer le reste.

Ce deux méthodes semblent différentes mais en réalité, les GC réalistes et optimisés s'approchent d'une forme hybride entre reference counting et tracing, cf. A Unified Theory of Garbage Collection.

Détection de pointeurs

Qu'ils soient tracing ou utilisant du reference counting, les GC doivent pouvoir identifier les pointeurs. Pour cela ils ont deux façon de procéder : soit ils essaient de deviner si une valeur est un pointeur, soit ils utilisent des métadonnées qui ont été ajoutées à la représentation à l'exécution de la valeur.

Deviner

On peut initialement supposer que toutes les valeurs sont des pointeurs. Puis on peut en éliminer certaines au moyen de quelques tests : cette valeur pointe-elle vers le tas ? est-ce qu'elle a été allouée par notre allocateur ? Cela suffit à se tromper assez rarement. Et si l'on se trompe, ce n'est pas si grave. Imaginons qu'un entier soit pris pour un pointeur vers une véritable zone mémoire que l'on a allouée ; elle ne sera simplement pas libérée tant que cet entier est là et on aura donc des fuites mémoires.

On parle de GC conservatifs. Un exemple connu est le GC de Boehm, cf. Garbage Collection in an Uncooperative Environment.

L'avantage de cette méthode est qu'elle ne nécessite aucune coopération de la part du compilateur. On peut prendre un compilateur C quelconque, remplacer les appels à malloc par des appels au malloc du GC de Boehm, supprimer les appels à free et cela suffit.

Métadonnées

Par opposition aux GCs conservatifs, il existe des GCs précis, qui identifient à coup sûr si une valeur est un pointeur ou non. Pour cela, le compilateur attache à la valeur des métadonnées qui seront conservées à l'exécution et permettront au GC de distinguer les pointeurs. Il existe différentes stratégies pour encoder ces métadonnées.

Après les pointeurs ?

La distinction des pointeurs n'est qu'une première étape, il existe ensuite plein de façons de libérer la mémoire, cf. Wilson, Uniprocessor Garbage Collection Techniques.

En OCaml

OCaml utilise un tracing GC, précis, avec deux générations, la première (tas mineur) utilisant un algorithme de stop&copy et la seconde (tas majeur) un algorithme mark&sweep avec compaction.

OCaml opère sur des valeurs de taille constante. Cette taille dépend du système :

# Sys.word_size;;
- : int = 64

Un bit est utilisé pour distinguer les pointeurs. Ainsi, les entiers sont plus petits que la taille des valeurs :

# Sys.int_size;;
- : int = 63

De nombreuses valeurs sont encodées comme des entiers :

# let is_int n = Obj.is_int (Obj.repr n);;
val is_int : 'a -> bool = <fun>
# is_int ();;
- : bool = true
# is_int 0;;
- : bool = true
# is_int 1;;
- : bool = true
# is_int 2;;
- : bool = true
# is_int 3;;
- : bool = true
# is_int 'a';;
- : bool = true
# is_int 'Z';;
- : bool = true
# is_int false;;
- : bool = true
# is_int true;;
- : bool = true
# is_int [];;
- : bool = true
# type t = A | B | C;;
type t = A | B | C
# is_int A;;
- : bool = true
# is_int B;;
- : bool = true
# is_int C;;
- : bool = true

Un bit n'étant plus disponible, un entier n est en fait représenté comme étant l'entier 2n + 1.

# let print_repr n = Format.printf "repr(%d) = %d@." (Obj.magic n) (2 * (Obj.magic n) + 1);;
val print_repr : 'a -> unit = <fun>
# print_repr 0;;
repr(0) = 1
- : unit = ()
# print_repr 1;;
repr(1) = 3
- : unit = ()
# print_repr 2;;
repr(2) = 5
- : unit = ()
# print_repr 3;;
repr(3) = 7
- : unit = ()
# print_repr ();;
repr(0) = 1
- : unit = ()
# print_repr 'a';;
repr(97) = 195
- : unit = ()
# print_repr 'Z';;
repr(90) = 181
- : unit = ()
# print_repr false;;
repr(0) = 1
- : unit = ()
# print_repr true;;
repr(1) = 3
- : unit = ()
# print_repr [];;
repr(0) = 1
- : unit = ()
# type t = A | B | C;;
type t = A | B | C
# print_repr A;;
repr(0) = 1
- : unit = ()
# print_repr B;;
repr(1) = 3
- : unit = ()
# print_repr C;;
repr(2) = 5
- : unit = ()

Si une valeur ne peut être encodée comme un entier (par exemple, parce qu'elle peut prendre plus de valeurs différentes que ce que permettent les entiers), alors, elle sera représentée par un pointeur vers un bloc alloué sur le tas.

Un block est composé de Sys.word_size - 10 bits indiquant la taille du bloc, de 2 bits permettant au GC de stocker la couleur du bloc, d'un tag indiquant de quel type de valeur est fait le bloc puis d'un ensemble de valeurs, chacune de taille Sys.word_size.

# let is_block v = Obj.is_block (Obj.repr v);;
val is_block : 'a -> bool = <fun>
# is_block 0;;
- : bool = false
# is_block ();;
- : bool = false
# is_block [];;
- : bool = false
# is_block [1; 2; 3];;
- : bool = true
# is_block [||];;
- : bool = true
# is_block [|1; 2; 3|];;
- : bool = true
# is_block 'a';;
- : bool = false
# is_block "coucou";;
- : bool = true
# is_block `Coucou;;
- : bool = false
# is_block (`Sava 666);;;
- : bool = true
# type t = A | B | C;;
type t = A | B | C
# is_block C;;
- : bool = false
# type t = A | B of float | C of int;;
type t = A | B of float | C of int
# is_block A;;
- : bool = false
# is_block (B 3.0);;
- : bool = true
# is_block (C 42);;
- : bool = true
# is_block 8.6;;
- : bool = true
# is_block (5, 1);;
- : bool = true
# type t = { x : int };;
type t = { x : int; }
# is_block { x = 1650 };;
- : bool = true
(* unsafe.ml *)

module Tag = struct
  type t =
    | Lazy
    | Closure
    | Object
    | Infix
    | Forward
    | No_scan
    | Abstract (* TODO: remove and use only no_scan_tag ? *)
    | String
    | Double
    | Double_array
    | Custom
    | Int
    | Out_of_heap
    | Unaligned
    | Unknown of int

  let to_int = function
    | Lazy -> Obj.lazy_tag
    | Closure -> Obj.closure_tag
    | Object -> Obj.object_tag
    | Infix -> Obj.infix_tag
    | Forward -> Obj.forward_tag
    | No_scan -> Obj.no_scan_tag
    | Abstract -> Obj.abstract_tag
    | String -> Obj.string_tag
    | Double -> Obj.double_tag
    | Double_array -> Obj.double_array_tag
    | Custom -> Obj.custom_tag
    | Int -> Obj.int_tag
    | Out_of_heap -> Obj.out_of_heap_tag
    | Unaligned -> Obj.unaligned_tag
    | Unknown n -> n

  let to_string = function
    | Lazy -> "lazy"
    | Closure -> "closure"
    | Object -> "object"
    | Infix -> "infix"
    | Forward -> "forward"
    | No_scan -> "no scan"
    | Abstract -> "abstract"
    | String -> "string"
    | Double -> "double"
    | Double_array -> "double array"
    | Custom -> "custom"
    | Int -> "int"
    | Out_of_heap -> "out of heap"
    | Unaligned -> "unaligned"
    | Unknown n -> string_of_int n

  let of_int = function
    | n when n = Obj.lazy_tag -> Lazy
    | n when n = Obj.closure_tag -> Closure
    | n when n = Obj.object_tag -> Object
    | n when n = Obj.infix_tag -> Infix
    | n when n = Obj.forward_tag -> Forward
    | n when n = Obj.no_scan_tag -> No_scan
    | n when n = Obj.abstract_tag -> Abstract
    | n when n = Obj.string_tag -> String
    | n when n = Obj.double_tag -> Double
    | n when n = Obj.double_array_tag -> Double_array
    | n when n = Obj.custom_tag -> Custom
    | n when n = Obj.int_tag -> Int
    | n when n = Obj.out_of_heap_tag -> Out_of_heap
    | n when n = Obj.unaligned_tag -> Unaligned
    | n -> Unknown n

  let should_be_scanned tag =
    to_int tag < Obj.no_scan_tag
end

module Value = struct

  type t =
    | Int of int
    | Block of (int * int * Tag.t)

  let of_v v =
    let v = Obj.repr v in
    if Obj.is_int v then
      let n : int = Obj.magic v in
      Int n
    else
      let p : int = Obj.magic v in
      let size = Obj.size v in
      let tag = Tag.of_int @@ Obj.tag v in
      Block (p, size, tag)

end

let rec print_info : 'a. string -> 'a -> unit = fun s v ->
  Format.printf "testing `%s`@." s;
  match Value.of_v v with
  | Int n ->
    let repr = 2 * n + 1 in
    Format.printf "it's an int !@.";
    Format.printf "it's %d !@." n;
    Format.printf "it's stored as %d !@." repr;
    Format.printf "@."
  | Block (p, size, tag) ->
    Format.printf "it's a block !@.";
    Format.printf "it's pointing to 0x%x !@." p;
    Format.printf "it has tag %s (%d) !@." (Tag.to_string tag) (Tag.to_int tag);
    Format.printf "it has size %d !@." size;
    Format.printf "@.";
    if Tag.should_be_scanned tag then begin
      for i = 0 to size - 1 do
        print_info (Format.sprintf "field %d of %s" i s) (Obj.field (Obj.repr v) i)
      done;
  end

module Tbl = Hashtbl.Make(struct
  type t = Obj.t
  let equal = (==)
  let hash = Hashtbl.hash
end)

let reachable_words v =
  let tbl = Tbl.create 64 in
  let rec aux v =
    if Tbl.mem tbl v then 0
    else begin
      Tbl.add tbl v ();
      match Value.of_v v with
      | Int _n -> 0
      | Block (_p, size, tag) ->
        let fields_size =
          if Tag.should_be_scanned tag then begin
            let fields_size = ref 0 in
            let start = match tag with
            | Closure -> 2
            | _ -> 0
            in
            for i = start to size - 1 do
              let field = Obj.field (Obj.repr v) i in
              fields_size := !fields_size + aux field
            done;
            !fields_size
          end
          else 0
        in
        1 + size + fields_size
    end
  in
  aux (Obj.repr v)

type t = A | B | C

type t' = D | E of int | F of float

type t'' = { x : int }

let () =
  print_info "()" ();
  print_info "0" 0;
  print_info "1" 1;
  print_info "2" 2;
  print_info "3" 3;
  print_info "'a'" 'a';
  print_info "'Z'" 'Z';
  print_info "false" false;
  print_info "true" true;
  print_info "[]" [];
  print_info "A" A;
  print_info "B" B;
  print_info "C" C;
  print_info "D" D;
  print_info "(E 51)" (E 51);
  print_info "(F 8.6)" (F 8.6);
  print_info "[1; 2; 3]" [1; 2; 3];
  print_info {|"coucou"|} "coucou";
  print_info {|(`Sava "ui&twa")|} (`Sava "ui&twa");
  print_info "(5, 1)" (5, 1);
  print_info "{ x = 1685 }" { x = 1685 };
  print_info "print_info" print_info;
  print_info {|(print_info "print_info" print_info)|} (print_info "print_info" print_info);
  print_info {|(fun x -> print_info "x" x)|} (fun x -> print_info "x" x);

type 'a tree =
  | Leaf
  | Node of 'a * 'a tree * 'a tree

type cycle = {
  mutable next : cycle;
  v : int;
}

let () =

  let l = List.init 10 (fun n -> n) in

  let t1 = Node (l, Leaf, Leaf) in
  print_info "t1" t1;
  let t2 = Node (l, t1, t1) in
  let t3 = Node (l, t2, t2) in

  let rw_l = reachable_words l in
  Format.eprintf "rw_l = %d@." rw_l;
  let rw_t1 = reachable_words t1 in
  let rw_t2 = reachable_words t2 in
  let rw_t3 = reachable_words t3 in

  let assert_rw s v =
    let rw_me = reachable_words v in
    Format.printf "reachable_words(%s) = %d@." s rw_me;
    let rw_obj = Obj.reachable_words (Obj.repr v) in
    if not @@ Int.equal rw_me rw_obj then begin
      Format.eprintf "for %s, I computed %d but Obj says it's %d@." s rw_me rw_obj;
      exit 1
    end
    else ()
  in

  assert_rw "l" l;
  assert_rw "t1" t1;
  assert_rw "t2" t2;
  assert_rw "t3" t3;

  let rec c = {
    next = c;
    v = 42;
  } in

  assert_rw "cycle" c;

  let f x = c.v + x in

  let z = 434 in

  let _f x = x + z in

  let rec f x =
    1 + g x * c.v
  and g x =
    2 + f x + c.v
  in

  let f = "fseljfseljflksej fkeljslfj " in

  let f = [|1. ; 2.3 ; 4.5432 |] in

  assert_rw "fun" f
$ ocamlopt unsafe.ml
File "unsafe.ml", line 203, characters 6-11:
203 |   let rw_t1 = reachable_words t1 in
            ^^^^^
Warning 26 [unused-var]: unused variable rw_t1.
File "unsafe.ml", line 204, characters 6-11:
204 |   let rw_t2 = reachable_words t2 in
            ^^^^^
Warning 26 [unused-var]: unused variable rw_t2.
File "unsafe.ml", line 205, characters 6-11:
205 |   let rw_t3 = reachable_words t3 in
            ^^^^^
Warning 26 [unused-var]: unused variable rw_t3.
File "unsafe.ml", line 230, characters 6-7:
230 |   let f x = c.v + x in
            ^
Warning 26 [unused-var]: unused variable f.
File "unsafe.ml", line 236, characters 10-11:
236 |   let rec f x =
                ^
Warning 26 [unused-var]: unused variable f.
File "unsafe.ml", line 238, characters 6-7:
238 |   and g x =
            ^
Warning 26 [unused-var]: unused variable g.
File "unsafe.ml", line 242, characters 6-7:
242 |   let f = "fseljfseljflksej fkeljslfj " in
            ^
Warning 26 [unused-var]: unused variable f.
$ ./a.out
testing `()`
it's an int !
it's 0 !
it's stored as 1 !

testing `0`
it's an int !
it's 0 !
it's stored as 1 !

testing `1`
it's an int !
it's 1 !
it's stored as 3 !

testing `2`
it's an int !
it's 2 !
it's stored as 5 !

testing `3`
it's an int !
it's 3 !
it's stored as 7 !

testing `'a'`
it's an int !
it's 97 !
it's stored as 195 !

testing `'Z'`
it's an int !
it's 90 !
it's stored as 181 !

testing `false`
it's an int !
it's 0 !
it's stored as 1 !

testing `true`
it's an int !
it's 1 !
it's stored as 3 !

testing `[]`
it's an int !
it's 0 !
it's stored as 1 !

testing `A`
it's an int !
it's 0 !
it's stored as 1 !

testing `B`
it's an int !
it's 1 !
it's stored as 3 !

testing `C`
it's an int !
it's 2 !
it's stored as 5 !

testing `D`
it's an int !
it's 0 !
it's stored as 1 !

testing `(E 51)`
it's a block !
it's pointing to 0x2aaaaab0b9f8 !
it has tag 0 (0) !
it has size 1 !

testing `field 0 of (E 51)`
it's an int !
it's 51 !
it's stored as 103 !

testing `(F 8.6)`
it's a block !
it's pointing to 0x2aaaaab0b9e0 !
it has tag 1 (1) !
it has size 1 !

testing `field 0 of (F 8.6)`
it's a block !
it's pointing to 0x2aaaaab0b9e8 !
it has tag double (253) !
it has size 1 !

testing `[1; 2; 3]`
it's a block !
it's pointing to 0x2aaaaab0b9a8 !
it has tag 0 (0) !
it has size 2 !

testing `field 0 of [1; 2; 3]`
it's an int !
it's 1 !
it's stored as 3 !

testing `field 1 of [1; 2; 3]`
it's a block !
it's pointing to 0x2aaaaab0b9b4 !
it has tag 0 (0) !
it has size 2 !

testing `field 0 of field 1 of [1; 2; 3]`
it's an int !
it's 2 !
it's stored as 5 !

testing `field 1 of field 1 of [1; 2; 3]`
it's a block !
it's pointing to 0x2aaaaab0b9c0 !
it has tag 0 (0) !
it has size 2 !

testing `field 0 of field 1 of field 1 of [1; 2; 3]`
it's an int !
it's 3 !
it's stored as 7 !

testing `field 1 of field 1 of field 1 of [1; 2; 3]`
it's an int !
it's 0 !
it's stored as 1 !

testing `"coucou"`
it's a block !
it's pointing to 0x2aaaaab0b994 !
it has tag string (252) !
it has size 1 !

testing `(`Sava "ui&twa")`
it's a block !
it's pointing to 0x2aaaaab0b970 !
it has tag 0 (0) !
it has size 2 !

testing `field 0 of (`Sava "ui&twa")`
it's an int !
it's 925284185 !
it's stored as 1850568371 !

testing `field 1 of (`Sava "ui&twa")`
it's a block !
it's pointing to 0x2aaaaab0b97c !
it has tag string (252) !
it has size 1 !

testing `(5, 1)`
it's a block !
it's pointing to 0x2aaaaab0b95c !
it has tag 0 (0) !
it has size 2 !

testing `field 0 of (5, 1)`
it's an int !
it's 5 !
it's stored as 11 !

testing `field 1 of (5, 1)`
it's an int !
it's 1 !
it's stored as 3 !

testing `{ x = 1685 }`
it's a block !
it's pointing to 0x2aaaaab0bf58 !
it has tag 0 (0) !
it has size 1 !

testing `field 0 of { x = 1685 }`
it's an int !
it's 1685 !
it's stored as 3371 !

testing `print_info`
it's a block !
it's pointing to 0x2aaaaab0b8c8 !
it has tag closure (247) !
it has size 3 !

testing `field 0 of print_info`
it's a block !
it's pointing to 0x2aaaaaad0678 !
it has tag out of heap (1001) !
it has size 8538260422657 !

testing `field 1 of print_info`
it's an int !
it's 72057594037927939 !
it's stored as 144115188075855879 !

testing `field 2 of print_info`
it's a block !
it's pointing to 0x2aaaaaad08d0 !
it has tag out of heap (1001) !
it has size 8538394984414 !

testing `print_info`
it's a block !
it's pointing to 0x2aaaaab0b8c8 !
it has tag closure (247) !
it has size 3 !

testing `field 0 of print_info`
it's a block !
it's pointing to 0x2aaaaaad0678 !
it has tag out of heap (1001) !
it has size 8538260422657 !

testing `field 1 of print_info`
it's an int !
it's 72057594037927939 !
it's stored as 144115188075855879 !

testing `field 2 of print_info`
it's a block !
it's pointing to 0x2aaaaaad08d0 !
it has tag out of heap (1001) !
it has size 8538394984414 !

testing `(print_info "print_info" print_info)`
it's an int !
it's 0 !
it's stored as 1 !

testing `(fun x -> print_info "x" x)`
it's a block !
it's pointing to 0x2aaaaab0b8e4 !
it has tag closure (247) !
it has size 2 !

testing `field 0 of (fun x -> print_info "x" x)`
it's a block !
it's pointing to 0x2aaaaaad0b28 !
it has tag out of heap (1001) !
it has size 8537321242624 !

testing `field 1 of (fun x -> print_info "x" x)`
it's an int !
it's 36028797018963970 !
it's stored as 72057594037927941 !

testing `t1`
it's a block !
it's pointing to 0x3ffffbe13910 !
it has tag 0 (0) !
it has size 3 !

testing `field 0 of t1`
it's a block !
it's pointing to 0x3ffffbe13920 !
it has tag 0 (0) !
it has size 2 !

testing `field 0 of field 0 of t1`
it's an int !
it's 0 !
it's stored as 1 !

testing `field 1 of field 0 of t1`
it's a block !
it's pointing to 0x3ffffbe1392c !
it has tag 0 (0) !
it has size 2 !

testing `field 0 of field 1 of field 0 of t1`
it's an int !
it's 1 !
it's stored as 3 !

testing `field 1 of field 1 of field 0 of t1`
it's a block !
it's pointing to 0x3ffffbe13938 !
it has tag 0 (0) !
it has size 2 !

testing `field 0 of field 1 of field 1 of field 0 of t1`
it's an int !
it's 2 !
it's stored as 5 !

testing `field 1 of field 1 of field 1 of field 0 of t1`
it's a block !
it's pointing to 0x3ffffbe13944 !
it has tag 0 (0) !
it has size 2 !

testing `field 0 of field 1 of field 1 of field 1 of field 0 of t1`
it's an int !
it's 3 !
it's stored as 7 !

testing `field 1 of field 1 of field 1 of field 1 of field 0 of t1`
it's a block !
it's pointing to 0x3ffffbe13950 !
it has tag 0 (0) !
it has size 2 !

testing `field 0 of field 1 of field 1 of field 1 of field 1 of field 0 of t1`
it's an int !
it's 4 !
it's stored as 9 !

testing `field 1 of field 1 of field 1 of field 1 of field 1 of field 0 of t1`
it's a block !
it's pointing to 0x3ffffbe1395c !
it has tag 0 (0) !
it has size 2 !

testing `field 0 of field 1 of field 1 of field 1 of field 1 of field 1 of field 0 of t1`
it's an int !
it's 5 !
it's stored as 11 !

testing `field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 0 of t1`
it's a block !
it's pointing to 0x3ffffbe13968 !
it has tag 0 (0) !
it has size 2 !

testing `field 0 of field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 0 of t1`
it's an int !
it's 6 !
it's stored as 13 !

testing `field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 0 of t1`
it's a block !
it's pointing to 0x3ffffbe13974 !
it has tag 0 (0) !
it has size 2 !

testing `field 0 of field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 0 of t1`
it's an int !
it's 7 !
it's stored as 15 !

testing `field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 0 of t1`
it's a block !
it's pointing to 0x3ffffbe13980 !
it has tag 0 (0) !
it has size 2 !

testing `field 0 of field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 0 of t1`
it's an int !
it's 8 !
it's stored as 17 !

testing `field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 0 of t1`
it's a block !
it's pointing to 0x3ffffbe1398c !
it has tag 0 (0) !
it has size 2 !

testing `field 0 of field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 0 of t1`
it's an int !
it's 9 !
it's stored as 19 !

testing `field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 1 of field 0 of t1`
it's an int !
it's 0 !
it's stored as 1 !

testing `field 1 of t1`
it's an int !
it's 0 !
it's stored as 1 !

testing `field 2 of t1`
it's an int !
it's 0 !
it's stored as 1 !

rw_l = 30
reachable_words(l) = 30
reachable_words(t1) = 34
reachable_words(t2) = 38
reachable_words(t3) = 42
reachable_words(cycle) = 3
reachable_words(fun) = 4

Python

See hell.py https://realpython.com/pointers-in-python/

Java

https://www.baeldung.com/java-memory-layout e208d9aa1f/src/hotspot/share/oops/oop.hpp (L56-L57) https://www.baeldung.com/java-stack-heap https://www.betsol.com/blog/java-memory-management-for-java-virtual-machine-jvm/ https://www.jmdoudoux.fr/java/dej/chap-gestion_memoire.htm https://www.toptal.com/java/hunting-memory-leaks-in-java https://www.cs.unc.edu/~dewan/comp401/s09/Class%20Notes/8_Pointers_Notes.pdf https://www.guru99.com/java-stack-heap.html https://prod-mpc.upgrad.com/blog/memory-allocation-in-java/ https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html

polymorphisme avec héritage multiple ?

JS

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management

.NET

Various

https://www2.southeastern.edu/Academics/Faculty/kyang/2014/Fall/CMPS401/ClassNotes/CMPS401ClassNotesChap06.pdf

MDX test

# 1 + 3;;
- : int = 4