1995-08-09 08:06:35 -07:00
|
|
|
/***********************************************************************/
|
|
|
|
/* */
|
2011-07-27 07:17:02 -07:00
|
|
|
/* OCaml */
|
1995-08-09 08:06:35 -07:00
|
|
|
/* */
|
|
|
|
/* Damien Doligez, projet Para, INRIA Rocquencourt */
|
|
|
|
/* */
|
1996-04-30 07:53:58 -07:00
|
|
|
/* Copyright 1996 Institut National de Recherche en Informatique et */
|
1999-11-17 10:59:06 -08:00
|
|
|
/* en Automatique. All rights reserved. This file is distributed */
|
2001-12-07 05:41:02 -08:00
|
|
|
/* under the terms of the GNU Library General Public License, with */
|
|
|
|
/* the special exception on linking described in file ../LICENSE. */
|
1995-08-09 08:06:35 -07:00
|
|
|
/* */
|
|
|
|
/***********************************************************************/
|
|
|
|
|
1998-10-07 12:01:42 -07:00
|
|
|
#include <limits.h>
|
|
|
|
|
1997-05-13 07:45:38 -07:00
|
|
|
#include "compact.h"
|
2000-02-10 06:04:59 -08:00
|
|
|
#include "custom.h"
|
1995-05-04 03:15:53 -07:00
|
|
|
#include "config.h"
|
|
|
|
#include "fail.h"
|
2000-01-07 08:51:58 -08:00
|
|
|
#include "finalise.h"
|
1995-05-04 03:15:53 -07:00
|
|
|
#include "freelist.h"
|
|
|
|
#include "gc.h"
|
|
|
|
#include "gc_ctrl.h"
|
|
|
|
#include "major_gc.h"
|
|
|
|
#include "misc.h"
|
|
|
|
#include "mlvalues.h"
|
|
|
|
#include "roots.h"
|
1997-02-24 11:24:39 -08:00
|
|
|
#include "weak.h"
|
1995-05-04 03:15:53 -07:00
|
|
|
|
2014-05-12 00:29:24 -07:00
|
|
|
#if defined (NATIVE_CODE) && defined (NO_NAKED_POINTERS)
|
|
|
|
#define NATIVE_CODE_AND_NO_NAKED_POINTERS
|
|
|
|
#else
|
|
|
|
#undef NATIVE_CODE_AND_NO_NAKED_POINTERS
|
|
|
|
#endif
|
|
|
|
|
2005-09-22 07:21:50 -07:00
|
|
|
uintnat caml_percent_free;
|
2009-11-04 04:25:47 -08:00
|
|
|
uintnat caml_major_heap_increment;
|
2008-01-03 01:37:10 -08:00
|
|
|
CAMLexport char *caml_heap_start;
|
2003-12-31 06:20:40 -08:00
|
|
|
char *caml_gc_sweep_hp;
|
|
|
|
int caml_gc_phase; /* always Phase_mark, Phase_sweep, or Phase_idle */
|
1995-05-04 03:15:53 -07:00
|
|
|
static value *gray_vals;
|
2003-12-31 06:20:40 -08:00
|
|
|
static value *gray_vals_cur, *gray_vals_end;
|
1995-05-04 03:15:53 -07:00
|
|
|
static asize_t gray_vals_size;
|
|
|
|
static int heap_is_pure; /* The heap is pure if the only gray objects
|
|
|
|
below [markhp] are also in [gray_vals]. */
|
2005-09-22 07:21:50 -07:00
|
|
|
uintnat caml_allocated_words;
|
|
|
|
uintnat caml_dependent_size, caml_dependent_allocated;
|
2004-06-14 08:17:43 -07:00
|
|
|
double caml_extra_heap_resources;
|
2014-12-24 12:18:22 -08:00
|
|
|
uintnat caml_fl_wsz_at_phase_change = 0;
|
2002-05-28 09:57:31 -07:00
|
|
|
|
2004-01-02 11:23:29 -08:00
|
|
|
extern char *caml_fl_merge; /* Defined in freelist.c. */
|
1995-05-04 03:15:53 -07:00
|
|
|
|
|
|
|
static char *markhp, *chunk, *limit;
|
|
|
|
|
2008-01-11 03:55:36 -08:00
|
|
|
int caml_gc_subphase; /* Subphase_{main,weak1,weak2,final} */
|
2002-02-05 09:11:33 -08:00
|
|
|
static value *weak_prev;
|
1997-02-24 11:24:39 -08:00
|
|
|
|
2008-01-11 03:55:36 -08:00
|
|
|
#ifdef DEBUG
|
|
|
|
static unsigned long major_gc_counter = 0;
|
|
|
|
#endif
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
static void realloc_gray_vals (void)
|
1995-05-04 03:15:53 -07:00
|
|
|
{
|
|
|
|
value *new;
|
|
|
|
|
|
|
|
Assert (gray_vals_cur == gray_vals_end);
|
2014-12-24 12:18:22 -08:00
|
|
|
if (gray_vals_size < caml_stat_heap_wsz / 32){
|
2005-09-22 07:21:50 -07:00
|
|
|
caml_gc_message (0x08, "Growing gray_vals to %"
|
|
|
|
ARCH_INTNAT_PRINTF_FORMAT "uk bytes\n",
|
|
|
|
(intnat) gray_vals_size * sizeof (value) / 512);
|
1995-05-04 03:15:53 -07:00
|
|
|
new = (value *) realloc ((char *) gray_vals,
|
|
|
|
2 * gray_vals_size * sizeof (value));
|
|
|
|
if (new == NULL){
|
2003-12-29 14:15:02 -08:00
|
|
|
caml_gc_message (0x08, "No room for growing gray_vals\n", 0);
|
1995-05-04 03:15:53 -07:00
|
|
|
gray_vals_cur = gray_vals;
|
|
|
|
heap_is_pure = 0;
|
|
|
|
}else{
|
|
|
|
gray_vals = new;
|
|
|
|
gray_vals_cur = gray_vals + gray_vals_size;
|
|
|
|
gray_vals_size *= 2;
|
|
|
|
gray_vals_end = gray_vals + gray_vals_size;
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
gray_vals_cur = gray_vals + gray_vals_size / 2;
|
|
|
|
heap_is_pure = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-12-31 06:20:40 -08:00
|
|
|
void caml_darken (value v, value *p /* not used */)
|
1995-05-04 03:15:53 -07:00
|
|
|
{
|
2014-05-12 00:29:24 -07:00
|
|
|
#ifdef NATIVE_CODE_AND_NO_NAKED_POINTERS
|
|
|
|
if (Is_block (v) && Wosize_val (v) > 0) {
|
|
|
|
/* We insist that naked pointers to outside the heap point to things that
|
|
|
|
look like values with headers coloured black. This isn't always
|
|
|
|
strictly necessary but is essential in certain cases---in particular
|
|
|
|
when the value is allocated in a read-only section. (For the values
|
|
|
|
where it would be safe it is a performance improvement since we avoid
|
|
|
|
putting them on the grey list.) */
|
|
|
|
CAMLassert (Is_in_heap (v) || Is_black_hd (Hd_val (v)));
|
|
|
|
#else
|
1995-07-10 02:48:27 -07:00
|
|
|
if (Is_block (v) && Is_in_heap (v)) {
|
2014-05-12 00:29:24 -07:00
|
|
|
#endif
|
2005-10-25 09:24:13 -07:00
|
|
|
header_t h = Hd_val (v);
|
|
|
|
tag_t t = Tag_hd (h);
|
|
|
|
if (t == Infix_tag){
|
|
|
|
v -= Infix_offset_val(v);
|
|
|
|
h = Hd_val (v);
|
|
|
|
t = Tag_hd (h);
|
|
|
|
}
|
|
|
|
CAMLassert (!Is_blue_hd (h));
|
|
|
|
if (Is_white_hd (h)){
|
|
|
|
if (t < No_scan_tag){
|
|
|
|
Hd_val (v) = Grayhd_hd (h);
|
|
|
|
*gray_vals_cur++ = v;
|
|
|
|
if (gray_vals_cur >= gray_vals_end) realloc_gray_vals ();
|
|
|
|
}else{
|
|
|
|
Hd_val (v) = Blackhd_hd (h);
|
|
|
|
}
|
1995-07-10 02:48:27 -07:00
|
|
|
}
|
1995-05-04 03:15:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
static void start_cycle (void)
|
1995-05-04 03:15:53 -07:00
|
|
|
{
|
2003-12-31 06:20:40 -08:00
|
|
|
Assert (caml_gc_phase == Phase_idle);
|
1995-05-04 03:15:53 -07:00
|
|
|
Assert (gray_vals_cur == gray_vals);
|
2003-12-29 14:15:02 -08:00
|
|
|
caml_gc_message (0x01, "Starting new major GC cycle\n", 0);
|
2004-01-01 08:42:43 -08:00
|
|
|
caml_darken_all_roots();
|
2003-12-31 06:20:40 -08:00
|
|
|
caml_gc_phase = Phase_mark;
|
2008-01-11 03:55:36 -08:00
|
|
|
caml_gc_subphase = Subphase_main;
|
1995-05-04 03:15:53 -07:00
|
|
|
markhp = NULL;
|
1999-11-08 09:05:45 -08:00
|
|
|
#ifdef DEBUG
|
2008-01-11 03:55:36 -08:00
|
|
|
++ major_gc_counter;
|
2004-01-02 11:23:29 -08:00
|
|
|
caml_heap_check ();
|
1999-11-08 09:05:45 -08:00
|
|
|
#endif
|
1995-05-04 03:15:53 -07:00
|
|
|
}
|
|
|
|
|
2005-09-22 07:21:50 -07:00
|
|
|
static void mark_slice (intnat work)
|
1995-05-04 03:15:53 -07:00
|
|
|
{
|
1997-05-13 07:45:38 -07:00
|
|
|
value *gray_vals_ptr; /* Local copy of gray_vals_cur */
|
1995-05-04 03:15:53 -07:00
|
|
|
value v, child;
|
1995-07-17 09:11:08 -07:00
|
|
|
header_t hd;
|
|
|
|
mlsize_t size, i;
|
2014-05-12 00:29:24 -07:00
|
|
|
#ifdef NATIVE_CODE_AND_NO_NAKED_POINTERS
|
|
|
|
int marking_closure = 0;
|
|
|
|
#endif
|
1995-05-04 03:15:53 -07:00
|
|
|
|
2003-12-29 14:15:02 -08:00
|
|
|
caml_gc_message (0x40, "Marking %ld words\n", work);
|
2008-01-11 03:55:36 -08:00
|
|
|
caml_gc_message (0x40, "Subphase = %ld\n", caml_gc_subphase);
|
1995-07-24 05:46:59 -07:00
|
|
|
gray_vals_ptr = gray_vals_cur;
|
1995-05-04 03:15:53 -07:00
|
|
|
while (work > 0){
|
1995-07-24 05:46:59 -07:00
|
|
|
if (gray_vals_ptr > gray_vals){
|
|
|
|
v = *--gray_vals_ptr;
|
1995-07-17 09:11:08 -07:00
|
|
|
hd = Hd_val(v);
|
2014-05-12 00:29:24 -07:00
|
|
|
#ifdef NATIVE_CODE_AND_NO_NAKED_POINTERS
|
|
|
|
marking_closure =
|
|
|
|
(Tag_hd (hd) == Closure_tag || Tag_hd (hd) == Infix_tag);
|
|
|
|
#endif
|
1995-07-17 09:11:08 -07:00
|
|
|
Assert (Is_gray_hd (hd));
|
|
|
|
Hd_val (v) = Blackhd_hd (hd);
|
2002-09-18 06:59:27 -07:00
|
|
|
size = Wosize_hd (hd);
|
1995-07-17 09:11:08 -07:00
|
|
|
if (Tag_hd (hd) < No_scan_tag){
|
1997-05-19 08:42:21 -07:00
|
|
|
for (i = 0; i < size; i++){
|
|
|
|
child = Field (v, i);
|
2014-05-12 00:29:24 -07:00
|
|
|
#ifdef NATIVE_CODE_AND_NO_NAKED_POINTERS
|
|
|
|
if (Is_block (child)
|
|
|
|
&& Wosize_val (child) > 0 /* Atoms never need to be marked. */
|
|
|
|
/* Closure blocks contain code pointers at offsets that cannot
|
|
|
|
be reliably determined, so we always use the page table when
|
|
|
|
marking such values. */
|
|
|
|
&& (!marking_closure || Is_in_heap (child))) {
|
|
|
|
/* See [caml_darken] for a description of this assertion. */
|
|
|
|
CAMLassert (Is_in_heap (child) || Is_black_hd (Hd_val (child)));
|
|
|
|
#else
|
1995-07-17 09:11:08 -07:00
|
|
|
if (Is_block (child) && Is_in_heap (child)) {
|
2014-05-12 00:29:24 -07:00
|
|
|
#endif
|
2002-09-18 06:59:27 -07:00
|
|
|
hd = Hd_val (child);
|
2002-01-20 09:39:10 -08:00
|
|
|
if (Tag_hd (hd) == Forward_tag){
|
2002-09-18 06:59:27 -07:00
|
|
|
value f = Forward_val (child);
|
2008-07-28 05:03:55 -07:00
|
|
|
if (Is_block (f)
|
|
|
|
&& (!Is_in_value_area(f) || Tag_val (f) == Forward_tag
|
|
|
|
|| Tag_val (f) == Lazy_tag || Tag_val (f) == Double_tag)){
|
2002-09-18 06:59:27 -07:00
|
|
|
/* Do not short-circuit the pointer. */
|
|
|
|
}else{
|
|
|
|
Field (v, i) = f;
|
|
|
|
}
|
2002-01-20 09:39:10 -08:00
|
|
|
}
|
2002-07-30 06:02:31 -07:00
|
|
|
else if (Tag_hd(hd) == Infix_tag) {
|
1995-07-17 09:11:08 -07:00
|
|
|
child -= Infix_offset_val(child);
|
|
|
|
hd = Hd_val(child);
|
|
|
|
}
|
|
|
|
if (Is_white_hd (hd)){
|
|
|
|
Hd_val (child) = Grayhd_hd (hd);
|
1995-07-24 05:46:59 -07:00
|
|
|
*gray_vals_ptr++ = child;
|
|
|
|
if (gray_vals_ptr >= gray_vals_end) {
|
|
|
|
gray_vals_cur = gray_vals_ptr;
|
|
|
|
realloc_gray_vals ();
|
|
|
|
gray_vals_ptr = gray_vals_cur;
|
|
|
|
}
|
1995-07-17 09:11:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1995-05-04 03:15:53 -07:00
|
|
|
}
|
1995-07-17 09:11:08 -07:00
|
|
|
work -= Whsize_wosize(size);
|
1995-05-04 03:15:53 -07:00
|
|
|
}else if (markhp != NULL){
|
|
|
|
if (markhp == limit){
|
1997-05-19 08:42:21 -07:00
|
|
|
chunk = Chunk_next (chunk);
|
|
|
|
if (chunk == NULL){
|
|
|
|
markhp = NULL;
|
|
|
|
}else{
|
|
|
|
markhp = chunk;
|
|
|
|
limit = chunk + Chunk_size (chunk);
|
|
|
|
}
|
1995-05-04 03:15:53 -07:00
|
|
|
}else{
|
1997-05-19 08:42:21 -07:00
|
|
|
if (Is_gray_val (Val_hp (markhp))){
|
|
|
|
Assert (gray_vals_ptr == gray_vals);
|
|
|
|
*gray_vals_ptr++ = Val_hp (markhp);
|
|
|
|
}
|
|
|
|
markhp += Bhsize_hp (markhp);
|
1995-05-04 03:15:53 -07:00
|
|
|
}
|
|
|
|
}else if (!heap_is_pure){
|
|
|
|
heap_is_pure = 1;
|
2003-12-31 06:20:40 -08:00
|
|
|
chunk = caml_heap_start;
|
1995-05-04 03:15:53 -07:00
|
|
|
markhp = chunk;
|
1997-05-13 07:45:38 -07:00
|
|
|
limit = chunk + Chunk_size (chunk);
|
2008-01-11 08:13:18 -08:00
|
|
|
}else{
|
|
|
|
switch (caml_gc_subphase){
|
|
|
|
case Subphase_main: {
|
|
|
|
/* The main marking phase is over. Start removing weak pointers to
|
|
|
|
dead values. */
|
|
|
|
caml_gc_subphase = Subphase_weak1;
|
|
|
|
weak_prev = &caml_weak_list_head;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Subphase_weak1: {
|
|
|
|
value cur, curfield;
|
|
|
|
mlsize_t sz, i;
|
|
|
|
header_t hd;
|
2000-01-07 08:51:58 -08:00
|
|
|
|
2008-01-11 08:13:18 -08:00
|
|
|
cur = *weak_prev;
|
|
|
|
if (cur != (value) NULL){
|
|
|
|
hd = Hd_val (cur);
|
2002-02-05 09:11:33 -08:00
|
|
|
sz = Wosize_hd (hd);
|
|
|
|
for (i = 1; i < sz; i++){
|
|
|
|
curfield = Field (cur, i);
|
2008-01-11 08:13:18 -08:00
|
|
|
weak_again:
|
2004-01-01 08:42:43 -08:00
|
|
|
if (curfield != caml_weak_none
|
2003-11-20 13:02:53 -08:00
|
|
|
&& Is_block (curfield) && Is_in_heap (curfield)){
|
2002-02-05 09:11:33 -08:00
|
|
|
if (Tag_val (curfield) == Forward_tag){
|
2002-09-18 06:59:27 -07:00
|
|
|
value f = Forward_val (curfield);
|
2008-07-28 05:03:55 -07:00
|
|
|
if (Is_block (f)) {
|
|
|
|
if (!Is_in_value_area(f) || Tag_val (f) == Forward_tag
|
|
|
|
|| Tag_val (f) == Lazy_tag || Tag_val (f) == Double_tag){
|
2002-09-18 06:59:27 -07:00
|
|
|
/* Do not short-circuit the pointer. */
|
|
|
|
}else{
|
|
|
|
Field (cur, i) = curfield = f;
|
|
|
|
goto weak_again;
|
|
|
|
}
|
2002-09-17 06:45:33 -07:00
|
|
|
}
|
2002-07-30 06:02:31 -07:00
|
|
|
}
|
2002-07-30 07:00:36 -07:00
|
|
|
if (Is_white_val (curfield)){
|
2004-01-01 08:42:43 -08:00
|
|
|
Field (cur, i) = caml_weak_none;
|
2002-02-05 09:11:33 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-01-11 08:13:18 -08:00
|
|
|
weak_prev = &Field (cur, 0);
|
|
|
|
work -= Whsize_hd (hd);
|
|
|
|
}else{
|
2012-06-21 07:30:11 -07:00
|
|
|
/* Subphase_weak1 is done.
|
|
|
|
Handle finalised values and start removing dead weak arrays. */
|
|
|
|
gray_vals_cur = gray_vals_ptr;
|
|
|
|
caml_final_update ();
|
|
|
|
gray_vals_ptr = gray_vals_cur;
|
2008-01-11 08:13:18 -08:00
|
|
|
caml_gc_subphase = Subphase_weak2;
|
|
|
|
weak_prev = &caml_weak_list_head;
|
|
|
|
}
|
2008-01-11 03:55:36 -08:00
|
|
|
}
|
2008-01-11 08:13:18 -08:00
|
|
|
break;
|
|
|
|
case Subphase_weak2: {
|
|
|
|
value cur;
|
|
|
|
header_t hd;
|
2008-01-11 03:55:36 -08:00
|
|
|
|
2008-01-11 08:13:18 -08:00
|
|
|
cur = *weak_prev;
|
|
|
|
if (cur != (value) NULL){
|
|
|
|
hd = Hd_val (cur);
|
|
|
|
if (Color_hd (hd) == Caml_white){
|
|
|
|
/* The whole array is dead, remove it from the list. */
|
|
|
|
*weak_prev = Field (cur, 0);
|
|
|
|
}else{
|
|
|
|
weak_prev = &Field (cur, 0);
|
|
|
|
}
|
|
|
|
work -= 1;
|
2008-01-11 03:55:36 -08:00
|
|
|
}else{
|
2012-06-21 07:30:11 -07:00
|
|
|
/* Subphase_weak2 is done. Go to Subphase_final. */
|
2008-01-11 08:13:18 -08:00
|
|
|
caml_gc_subphase = Subphase_final;
|
2002-02-05 09:11:33 -08:00
|
|
|
}
|
2008-01-11 08:13:18 -08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Subphase_final: {
|
|
|
|
/* Initialise the sweep phase. */
|
2002-02-05 09:11:33 -08:00
|
|
|
gray_vals_cur = gray_vals_ptr;
|
2008-01-11 08:13:18 -08:00
|
|
|
caml_gc_sweep_hp = caml_heap_start;
|
|
|
|
caml_fl_init_merge ();
|
|
|
|
caml_gc_phase = Phase_sweep;
|
|
|
|
chunk = caml_heap_start;
|
|
|
|
caml_gc_sweep_hp = chunk;
|
|
|
|
limit = chunk + Chunk_size (chunk);
|
|
|
|
work = 0;
|
2014-12-24 12:18:22 -08:00
|
|
|
caml_fl_wsz_at_phase_change = caml_fl_cur_wsz;
|
2008-01-11 08:13:18 -08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default: Assert (0);
|
2002-02-05 09:11:33 -08:00
|
|
|
}
|
1995-05-04 03:15:53 -07:00
|
|
|
}
|
|
|
|
}
|
1995-07-24 05:46:59 -07:00
|
|
|
gray_vals_cur = gray_vals_ptr;
|
1995-05-04 03:15:53 -07:00
|
|
|
}
|
|
|
|
|
2005-09-22 07:21:50 -07:00
|
|
|
static void sweep_slice (intnat work)
|
1995-05-04 03:15:53 -07:00
|
|
|
{
|
|
|
|
char *hp;
|
|
|
|
header_t hd;
|
|
|
|
|
2003-12-29 14:15:02 -08:00
|
|
|
caml_gc_message (0x40, "Sweeping %ld words\n", work);
|
1995-05-04 03:15:53 -07:00
|
|
|
while (work > 0){
|
2003-12-31 06:20:40 -08:00
|
|
|
if (caml_gc_sweep_hp < limit){
|
|
|
|
hp = caml_gc_sweep_hp;
|
1995-05-04 03:15:53 -07:00
|
|
|
hd = Hd_hp (hp);
|
|
|
|
work -= Whsize_hd (hd);
|
2003-12-31 06:20:40 -08:00
|
|
|
caml_gc_sweep_hp += Bhsize_hd (hd);
|
1995-05-04 03:15:53 -07:00
|
|
|
switch (Color_hd (hd)){
|
2000-01-02 08:10:00 -08:00
|
|
|
case Caml_white:
|
2000-02-10 06:04:59 -08:00
|
|
|
if (Tag_hd (hd) == Custom_tag){
|
2000-02-11 04:03:31 -08:00
|
|
|
void (*final_fun)(value) = Custom_ops_val(Val_hp(hp))->finalize;
|
2000-02-10 06:04:59 -08:00
|
|
|
if (final_fun != NULL) final_fun(Val_hp(hp));
|
1997-05-19 08:42:21 -07:00
|
|
|
}
|
2014-12-16 11:36:35 -08:00
|
|
|
caml_gc_sweep_hp = (char *) caml_fl_merge_block (Val_hp (hp));
|
1997-05-19 08:42:21 -07:00
|
|
|
break;
|
2000-01-02 08:10:00 -08:00
|
|
|
case Caml_blue:
|
1997-05-19 08:42:21 -07:00
|
|
|
/* Only the blocks of the free-list are blue. See [freelist.c]. */
|
2004-01-02 11:23:29 -08:00
|
|
|
caml_fl_merge = Bp_hp (hp);
|
1997-05-19 08:42:21 -07:00
|
|
|
break;
|
2000-01-02 08:10:00 -08:00
|
|
|
default: /* gray or black */
|
|
|
|
Assert (Color_hd (hd) == Caml_black);
|
1997-05-19 08:42:21 -07:00
|
|
|
Hd_hp (hp) = Whitehd_hd (hd);
|
|
|
|
break;
|
1995-05-04 03:15:53 -07:00
|
|
|
}
|
2003-12-31 06:20:40 -08:00
|
|
|
Assert (caml_gc_sweep_hp <= limit);
|
1995-05-04 03:15:53 -07:00
|
|
|
}else{
|
1997-05-13 07:45:38 -07:00
|
|
|
chunk = Chunk_next (chunk);
|
1995-05-04 03:15:53 -07:00
|
|
|
if (chunk == NULL){
|
1997-05-19 08:42:21 -07:00
|
|
|
/* Sweeping is done. */
|
2004-01-02 11:23:29 -08:00
|
|
|
++ caml_stat_major_collections;
|
1997-05-13 07:45:38 -07:00
|
|
|
work = 0;
|
2003-12-31 06:20:40 -08:00
|
|
|
caml_gc_phase = Phase_idle;
|
1995-05-04 03:15:53 -07:00
|
|
|
}else{
|
2003-12-31 06:20:40 -08:00
|
|
|
caml_gc_sweep_hp = chunk;
|
1997-05-19 08:42:21 -07:00
|
|
|
limit = chunk + Chunk_size (chunk);
|
1995-05-04 03:15:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-02-05 09:11:33 -08:00
|
|
|
/* The main entry point for the GC. Called after each minor GC.
|
|
|
|
[howmuch] is the amount of work to do, 0 to let the GC compute it.
|
|
|
|
Return the computed amount of work to do.
|
|
|
|
*/
|
2005-09-22 07:21:50 -07:00
|
|
|
intnat caml_major_collection_slice (intnat howmuch)
|
1995-05-04 03:15:53 -07:00
|
|
|
{
|
2004-06-14 08:17:43 -07:00
|
|
|
double p, dp;
|
2005-09-22 07:21:50 -07:00
|
|
|
intnat computed_work;
|
2000-01-07 08:51:58 -08:00
|
|
|
/*
|
1997-05-21 08:28:15 -07:00
|
|
|
Free memory at the start of the GC cycle (garbage + free list) (assumed):
|
2014-12-24 12:18:22 -08:00
|
|
|
FM = caml_stat_heap_wsz * caml_percent_free
|
2003-12-31 06:20:40 -08:00
|
|
|
/ (100 + caml_percent_free)
|
2002-06-05 05:11:15 -07:00
|
|
|
|
|
|
|
Assuming steady state and enforcing a constant allocation rate, then
|
|
|
|
FM is divided in 2/3 for garbage and 1/3 for free list.
|
|
|
|
G = 2 * FM / 3
|
2004-06-14 08:17:43 -07:00
|
|
|
G is also the amount of memory that will be used during this cycle
|
2002-06-05 05:11:15 -07:00
|
|
|
(still assuming steady state).
|
|
|
|
|
|
|
|
Proportion of G consumed since the previous slice:
|
2003-12-31 06:20:40 -08:00
|
|
|
PH = caml_allocated_words / G
|
|
|
|
= caml_allocated_words * 3 * (100 + caml_percent_free)
|
2014-12-24 12:18:22 -08:00
|
|
|
/ (2 * caml_stat_heap_wsz * caml_percent_free)
|
2004-06-14 08:17:43 -07:00
|
|
|
Proportion of extra-heap resources consumed since the previous slice:
|
|
|
|
PE = caml_extra_heap_resources
|
1995-05-04 03:15:53 -07:00
|
|
|
Proportion of total work to do in this slice:
|
1998-08-07 11:43:39 -07:00
|
|
|
P = max (PH, PE)
|
1995-05-04 03:15:53 -07:00
|
|
|
Amount of marking work for the GC cycle:
|
2014-12-24 12:18:22 -08:00
|
|
|
MW = caml_stat_heap_wsz * 100 / (100 + caml_percent_free)
|
1995-05-04 03:15:53 -07:00
|
|
|
Amount of sweeping work for the GC cycle:
|
2014-12-24 12:18:22 -08:00
|
|
|
SW = caml_stat_heap_wsz
|
2008-12-03 10:09:09 -08:00
|
|
|
|
|
|
|
In order to finish marking with a non-empty free list, we will
|
|
|
|
use 40% of the time for marking, and 60% for sweeping.
|
|
|
|
|
|
|
|
If TW is the total work for this cycle,
|
|
|
|
MW = 40/100 * TW
|
|
|
|
SW = 60/100 * TW
|
|
|
|
|
|
|
|
Amount of work to do for this slice:
|
|
|
|
W = P * TW
|
|
|
|
|
|
|
|
Amount of marking work for a marking slice:
|
|
|
|
MS = P * MW / (40/100)
|
2014-12-24 12:18:22 -08:00
|
|
|
MS = P * caml_stat_heap_wsz * 250 / (100 + caml_percent_free)
|
2008-12-03 10:09:09 -08:00
|
|
|
Amount of sweeping work for a sweeping slice:
|
|
|
|
SS = P * SW / (60/100)
|
2014-12-24 12:18:22 -08:00
|
|
|
SS = P * caml_stat_heap_wsz * 5 / 3
|
2008-12-03 10:09:09 -08:00
|
|
|
|
|
|
|
This slice will either mark MS words or sweep SS words.
|
1995-05-04 03:15:53 -07:00
|
|
|
*/
|
|
|
|
|
2003-12-31 06:20:40 -08:00
|
|
|
if (caml_gc_phase == Phase_idle) start_cycle ();
|
1997-05-13 07:45:38 -07:00
|
|
|
|
2003-12-31 06:20:40 -08:00
|
|
|
p = (double) caml_allocated_words * 3.0 * (100 + caml_percent_free)
|
2014-12-24 12:18:22 -08:00
|
|
|
/ caml_stat_heap_wsz / caml_percent_free / 2.0;
|
2004-06-14 08:17:43 -07:00
|
|
|
if (caml_dependent_size > 0){
|
|
|
|
dp = (double) caml_dependent_allocated * (100 + caml_percent_free)
|
|
|
|
/ caml_dependent_size / caml_percent_free;
|
|
|
|
}else{
|
|
|
|
dp = 0.0;
|
|
|
|
}
|
|
|
|
if (p < dp) p = dp;
|
|
|
|
if (p < caml_extra_heap_resources) p = caml_extra_heap_resources;
|
1998-08-07 11:43:39 -07:00
|
|
|
|
2008-01-11 03:55:36 -08:00
|
|
|
caml_gc_message (0x40, "allocated_words = %"
|
2005-09-22 07:21:50 -07:00
|
|
|
ARCH_INTNAT_PRINTF_FORMAT "u\n",
|
|
|
|
caml_allocated_words);
|
|
|
|
caml_gc_message (0x40, "extra_heap_resources = %"
|
|
|
|
ARCH_INTNAT_PRINTF_FORMAT "uu\n",
|
|
|
|
(uintnat) (caml_extra_heap_resources * 1000000));
|
|
|
|
caml_gc_message (0x40, "amount of work to do = %"
|
|
|
|
ARCH_INTNAT_PRINTF_FORMAT "uu\n",
|
|
|
|
(uintnat) (p * 1000000));
|
1998-08-07 11:43:39 -07:00
|
|
|
|
2003-12-31 06:20:40 -08:00
|
|
|
if (caml_gc_phase == Phase_mark){
|
2014-12-24 12:18:22 -08:00
|
|
|
computed_work = (intnat) (p * caml_stat_heap_wsz * 250
|
2008-12-03 10:09:09 -08:00
|
|
|
/ (100 + caml_percent_free));
|
2002-02-05 09:11:33 -08:00
|
|
|
}else{
|
2014-12-24 12:18:22 -08:00
|
|
|
computed_work = (intnat) (p * caml_stat_heap_wsz * 5 / 3);
|
2002-02-05 09:11:33 -08:00
|
|
|
}
|
2003-12-29 14:15:02 -08:00
|
|
|
caml_gc_message (0x40, "ordered work = %ld words\n", howmuch);
|
|
|
|
caml_gc_message (0x40, "computed work = %ld words\n", computed_work);
|
2002-02-05 09:11:33 -08:00
|
|
|
if (howmuch == 0) howmuch = computed_work;
|
2003-12-31 06:20:40 -08:00
|
|
|
if (caml_gc_phase == Phase_mark){
|
2002-02-05 09:11:33 -08:00
|
|
|
mark_slice (howmuch);
|
2003-12-29 14:15:02 -08:00
|
|
|
caml_gc_message (0x02, "!", 0);
|
1995-05-04 03:15:53 -07:00
|
|
|
}else{
|
2003-12-31 06:20:40 -08:00
|
|
|
Assert (caml_gc_phase == Phase_sweep);
|
2002-02-05 09:11:33 -08:00
|
|
|
sweep_slice (howmuch);
|
2003-12-29 14:15:02 -08:00
|
|
|
caml_gc_message (0x02, "$", 0);
|
1995-05-04 03:15:53 -07:00
|
|
|
}
|
1997-05-13 07:45:38 -07:00
|
|
|
|
2003-12-31 06:20:40 -08:00
|
|
|
if (caml_gc_phase == Phase_idle) caml_compact_heap_maybe ();
|
1997-05-13 07:45:38 -07:00
|
|
|
|
2004-01-02 11:23:29 -08:00
|
|
|
caml_stat_major_words += caml_allocated_words;
|
2003-12-31 06:20:40 -08:00
|
|
|
caml_allocated_words = 0;
|
2004-06-14 08:17:43 -07:00
|
|
|
caml_dependent_allocated = 0;
|
|
|
|
caml_extra_heap_resources = 0.0;
|
2002-02-05 09:11:33 -08:00
|
|
|
return computed_work;
|
1995-05-04 03:15:53 -07:00
|
|
|
}
|
|
|
|
|
2000-01-07 08:51:58 -08:00
|
|
|
/* The minor heap must be empty when this function is called;
|
|
|
|
the minor heap is empty when this function returns.
|
|
|
|
*/
|
2003-12-31 06:20:40 -08:00
|
|
|
/* This does not call caml_compact_heap_maybe because the estimations of
|
1997-05-13 07:45:38 -07:00
|
|
|
free and live memory are only valid for a cycle done incrementally.
|
2003-12-31 06:20:40 -08:00
|
|
|
Besides, this function is called by caml_compact_heap_maybe.
|
1997-05-13 07:45:38 -07:00
|
|
|
*/
|
2003-12-31 06:20:40 -08:00
|
|
|
void caml_finish_major_cycle (void)
|
1995-05-04 03:15:53 -07:00
|
|
|
{
|
2003-12-31 06:20:40 -08:00
|
|
|
if (caml_gc_phase == Phase_idle) start_cycle ();
|
|
|
|
while (caml_gc_phase == Phase_mark) mark_slice (LONG_MAX);
|
|
|
|
Assert (caml_gc_phase == Phase_sweep);
|
|
|
|
while (caml_gc_phase == Phase_sweep) sweep_slice (LONG_MAX);
|
|
|
|
Assert (caml_gc_phase == Phase_idle);
|
2004-01-02 11:23:29 -08:00
|
|
|
caml_stat_major_words += caml_allocated_words;
|
2003-12-31 06:20:40 -08:00
|
|
|
caml_allocated_words = 0;
|
1995-05-04 03:15:53 -07:00
|
|
|
}
|
|
|
|
|
2002-12-15 15:27:06 -08:00
|
|
|
/* Make sure the request is at least Heap_chunk_min and round it up
|
2002-05-16 08:06:31 -07:00
|
|
|
to a multiple of the page size.
|
2014-12-24 12:18:22 -08:00
|
|
|
The argument and result are both numbers of words.
|
2002-05-16 08:06:31 -07:00
|
|
|
*/
|
|
|
|
static asize_t clip_heap_chunk_size (asize_t request)
|
2002-12-15 15:27:06 -08:00
|
|
|
{
|
2014-12-24 12:18:22 -08:00
|
|
|
if (request < Heap_chunk_min){
|
|
|
|
request = Heap_chunk_min;
|
2002-05-16 08:06:31 -07:00
|
|
|
}
|
2014-12-24 12:18:22 -08:00
|
|
|
return
|
|
|
|
Wsize_bsize (((Bsize_wsize (request) + Page_size - 1) >> Page_log) << Page_log);
|
2002-05-16 08:06:31 -07:00
|
|
|
}
|
|
|
|
|
2014-02-20 05:04:01 -08:00
|
|
|
/* Compute the heap increment, make sure the request is at least that big,
|
|
|
|
then call clip_heap_chunk_size, then make sure the result is >= request.
|
2014-12-24 12:18:22 -08:00
|
|
|
The argument and result are both numbers of words.
|
2002-05-16 08:06:31 -07:00
|
|
|
*/
|
2014-12-24 12:18:22 -08:00
|
|
|
asize_t caml_round_heap_chunk_wsz (asize_t request)
|
2002-05-16 08:06:31 -07:00
|
|
|
{
|
|
|
|
asize_t result = request;
|
2014-02-20 05:04:01 -08:00
|
|
|
uintnat incr;
|
2002-05-16 08:06:31 -07:00
|
|
|
|
2014-12-24 12:18:22 -08:00
|
|
|
/* Compute the heap increment as a word size. */
|
2014-02-20 05:04:01 -08:00
|
|
|
if (caml_major_heap_increment > 1000){
|
2014-12-24 12:18:22 -08:00
|
|
|
incr = caml_major_heap_increment;
|
2014-02-20 05:04:01 -08:00
|
|
|
}else{
|
2014-12-24 12:18:22 -08:00
|
|
|
incr = caml_stat_heap_wsz / 100 * caml_major_heap_increment;
|
2014-02-20 05:04:01 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (result < incr){
|
|
|
|
result = incr;
|
2002-05-16 08:06:31 -07:00
|
|
|
}
|
|
|
|
result = clip_heap_chunk_size (result);
|
|
|
|
|
|
|
|
if (result < request){
|
2004-01-01 08:42:43 -08:00
|
|
|
caml_raise_out_of_memory ();
|
2002-05-16 08:06:31 -07:00
|
|
|
return 0; /* not reached */
|
1995-05-04 03:15:53 -07:00
|
|
|
}
|
2002-05-16 08:06:31 -07:00
|
|
|
return result;
|
1995-05-04 03:15:53 -07:00
|
|
|
}
|
|
|
|
|
2014-12-12 07:18:04 -08:00
|
|
|
/* [heap_size] is a number of bytes */
|
2003-12-31 06:20:40 -08:00
|
|
|
void caml_init_major_heap (asize_t heap_size)
|
1995-05-04 03:15:53 -07:00
|
|
|
{
|
2014-12-24 12:18:22 -08:00
|
|
|
caml_stat_heap_wsz = Wsize_bsize (clip_heap_chunk_size (heap_size));
|
|
|
|
caml_stat_top_heap_wsz = caml_stat_heap_wsz;
|
|
|
|
Assert (Bsize_wsize (caml_stat_heap_wsz) % Page_size == 0);
|
|
|
|
caml_heap_start = (char *) caml_alloc_for_heap (Bsize_wsize (caml_stat_heap_wsz));
|
2003-12-31 06:20:40 -08:00
|
|
|
if (caml_heap_start == NULL)
|
2003-12-29 14:15:02 -08:00
|
|
|
caml_fatal_error ("Fatal error: not enough memory for the initial heap.\n");
|
2003-12-31 06:20:40 -08:00
|
|
|
Chunk_next (caml_heap_start) = NULL;
|
2004-01-02 11:23:29 -08:00
|
|
|
caml_stat_heap_chunks = 1;
|
2002-05-28 09:57:31 -07:00
|
|
|
|
2008-01-03 01:37:10 -08:00
|
|
|
if (caml_page_table_add(In_heap, caml_heap_start,
|
2014-12-24 12:18:22 -08:00
|
|
|
caml_heap_start + Bsize_wsize (caml_stat_heap_wsz)) != 0) {
|
2013-03-09 14:38:52 -08:00
|
|
|
caml_fatal_error ("Fatal error: not enough memory "
|
|
|
|
"for the initial page table.\n");
|
1995-05-04 03:15:53 -07:00
|
|
|
}
|
1998-08-07 11:43:39 -07:00
|
|
|
|
2004-01-02 11:23:29 -08:00
|
|
|
caml_fl_init_merge ();
|
|
|
|
caml_make_free_blocks ((value *) caml_heap_start,
|
2014-12-24 12:18:22 -08:00
|
|
|
caml_stat_heap_wsz, 1, Caml_white);
|
2003-12-31 06:20:40 -08:00
|
|
|
caml_gc_phase = Phase_idle;
|
1995-05-04 03:15:53 -07:00
|
|
|
gray_vals_size = 2048;
|
|
|
|
gray_vals = (value *) malloc (gray_vals_size * sizeof (value));
|
1995-08-08 06:37:34 -07:00
|
|
|
if (gray_vals == NULL)
|
2008-01-03 01:37:10 -08:00
|
|
|
caml_fatal_error ("Fatal error: not enough memory for the gray cache.\n");
|
1995-05-04 03:15:53 -07:00
|
|
|
gray_vals_cur = gray_vals;
|
|
|
|
gray_vals_end = gray_vals + gray_vals_size;
|
|
|
|
heap_is_pure = 1;
|
2003-12-31 06:20:40 -08:00
|
|
|
caml_allocated_words = 0;
|
2004-06-14 08:17:43 -07:00
|
|
|
caml_extra_heap_resources = 0.0;
|
1995-05-04 03:15:53 -07:00
|
|
|
}
|