version 1.1, 1999/11/27 10:58:32 |
version 1.1.1.3, 2000/12/01 14:48:23 |
|
|
/* |
/* |
* Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers |
* Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers |
* Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. |
* Copyright (c) 1991-1996 by Xerox Corporation. All rights reserved. |
* Copyright (c) 1998 by Silicon Graphics. All rights reserved. |
* Copyright (c) 1998 by Silicon Graphics. All rights reserved. |
|
* Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved. |
* |
* |
* THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED |
* THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED |
* OR IMPLIED. ANY USE IS AT YOUR OWN RISK. |
* OR IMPLIED. ANY USE IS AT YOUR OWN RISK. |
Line 62 word GC_gc_no = 0; |
|
Line 63 word GC_gc_no = 0; |
|
int GC_incremental = 0; /* By default, stop the world. */ |
int GC_incremental = 0; /* By default, stop the world. */ |
#endif |
#endif |
|
|
int GC_full_freq = 4; /* Every 5th collection is a full */ |
int GC_full_freq = 19; /* Every 20th collection is a full */ |
/* collection. */ |
/* collection, whether we need it */ |
|
/* or not. */ |
|
|
|
GC_bool GC_need_full_gc = FALSE; |
|
/* Need full GC do to heap growth. */ |
|
|
|
word GC_used_heap_size_after_full = 0; |
|
|
char * GC_copyright[] = |
char * GC_copyright[] = |
{"Copyright 1988,1989 Hans-J. Boehm and Alan J. Demers ", |
{"Copyright 1988,1989 Hans-J. Boehm and Alan J. Demers ", |
"Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. ", |
"Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. ", |
Line 82 extern signed_word GC_mem_found; /* Number of reclaim |
|
Line 89 extern signed_word GC_mem_found; /* Number of reclaim |
|
|
|
GC_bool GC_dont_expand = 0; |
GC_bool GC_dont_expand = 0; |
|
|
word GC_free_space_divisor = 4; |
word GC_free_space_divisor = 3; |
|
|
extern GC_bool GC_collection_in_progress(); |
extern GC_bool GC_collection_in_progress(); |
/* Collection is in progress, or was abandoned. */ |
/* Collection is in progress, or was abandoned. */ |
Line 130 static word min_words_allocd() |
|
Line 137 static word min_words_allocd() |
|
int dummy; |
int dummy; |
register signed_word stack_size = (ptr_t)(&dummy) - GC_stackbottom; |
register signed_word stack_size = (ptr_t)(&dummy) - GC_stackbottom; |
# endif |
# endif |
register word total_root_size; /* includes double stack size, */ |
word total_root_size; /* includes double stack size, */ |
/* since the stack is expensive */ |
/* since the stack is expensive */ |
/* to scan. */ |
/* to scan. */ |
|
word scan_size; /* Estimate of memory to be scanned */ |
|
/* during normal GC. */ |
|
|
if (stack_size < 0) stack_size = -stack_size; |
if (stack_size < 0) stack_size = -stack_size; |
total_root_size = 2 * stack_size + GC_root_size; |
total_root_size = 2 * stack_size + GC_root_size; |
|
scan_size = BYTES_TO_WORDS(GC_heapsize - GC_large_free_bytes |
|
+ (GC_large_free_bytes >> 2) |
|
/* use a bit more of large empty heap */ |
|
+ total_root_size); |
if (GC_incremental) { |
if (GC_incremental) { |
return(BYTES_TO_WORDS(GC_heapsize + total_root_size) |
return scan_size / (2 * GC_free_space_divisor); |
/ (2 * GC_free_space_divisor)); |
|
} else { |
} else { |
return(BYTES_TO_WORDS(GC_heapsize + total_root_size) |
return scan_size / GC_free_space_divisor; |
/ GC_free_space_divisor); |
|
} |
} |
} |
} |
|
|
Line 206 GC_bool GC_should_collect() |
|
Line 217 GC_bool GC_should_collect() |
|
return(GC_adj_words_allocd() >= min_words_allocd()); |
return(GC_adj_words_allocd() >= min_words_allocd()); |
} |
} |
|
|
|
|
void GC_notify_full_gc() |
void GC_notify_full_gc() |
{ |
{ |
if (GC_start_call_back != (void (*)())0) { |
if (GC_start_call_back != (void (*)())0) { |
Line 213 void GC_notify_full_gc() |
|
Line 225 void GC_notify_full_gc() |
|
} |
} |
} |
} |
|
|
|
GC_bool GC_is_full_gc = FALSE; |
|
|
/* |
/* |
* Initiate a garbage collection if appropriate. |
* Initiate a garbage collection if appropriate. |
* Choose judiciously |
* Choose judiciously |
Line 222 void GC_notify_full_gc() |
|
Line 236 void GC_notify_full_gc() |
|
void GC_maybe_gc() |
void GC_maybe_gc() |
{ |
{ |
static int n_partial_gcs = 0; |
static int n_partial_gcs = 0; |
GC_bool is_full_gc = FALSE; |
|
|
|
if (GC_should_collect()) { |
if (GC_should_collect()) { |
if (!GC_incremental) { |
if (!GC_incremental) { |
Line 230 void GC_maybe_gc() |
|
Line 243 void GC_maybe_gc() |
|
GC_gcollect_inner(); |
GC_gcollect_inner(); |
n_partial_gcs = 0; |
n_partial_gcs = 0; |
return; |
return; |
} else if (n_partial_gcs >= GC_full_freq) { |
} else if (GC_need_full_gc || n_partial_gcs >= GC_full_freq) { |
# ifdef PRINTSTATS |
# ifdef PRINTSTATS |
GC_printf2( |
GC_printf2( |
"***>Full mark for collection %lu after %ld allocd bytes\n", |
"***>Full mark for collection %lu after %ld allocd bytes\n", |
Line 242 void GC_maybe_gc() |
|
Line 255 void GC_maybe_gc() |
|
GC_clear_marks(); |
GC_clear_marks(); |
n_partial_gcs = 0; |
n_partial_gcs = 0; |
GC_notify_full_gc(); |
GC_notify_full_gc(); |
is_full_gc = TRUE; |
GC_is_full_gc = TRUE; |
} else { |
} else { |
n_partial_gcs++; |
n_partial_gcs++; |
} |
} |
Line 256 void GC_maybe_gc() |
|
Line 269 void GC_maybe_gc() |
|
# endif |
# endif |
GC_finish_collection(); |
GC_finish_collection(); |
} else { |
} else { |
if (!is_full_gc) { |
if (!GC_is_full_gc) { |
/* Count this as the first attempt */ |
/* Count this as the first attempt */ |
GC_n_attempts++; |
GC_n_attempts++; |
} |
} |
Line 293 GC_stop_func stop_func; |
|
Line 306 GC_stop_func stop_func; |
|
/* Make sure all blocks have been reclaimed, so sweep routines */ |
/* Make sure all blocks have been reclaimed, so sweep routines */ |
/* don't see cleared mark bits. */ |
/* don't see cleared mark bits. */ |
/* If we're guaranteed to finish, then this is unnecessary. */ |
/* If we're guaranteed to finish, then this is unnecessary. */ |
if (stop_func != GC_never_stop_func |
/* In the find_leak case, we have to finish to guarantee that */ |
|
/* previously unmarked objects are not reported as leaks. */ |
|
if ((GC_find_leak || stop_func != GC_never_stop_func) |
&& !GC_reclaim_all(stop_func, FALSE)) { |
&& !GC_reclaim_all(stop_func, FALSE)) { |
/* Aborted. So far everything is still consistent. */ |
/* Aborted. So far everything is still consistent. */ |
return(FALSE); |
return(FALSE); |
Line 303 GC_stop_func stop_func; |
|
Line 318 GC_stop_func stop_func; |
|
# ifdef SAVE_CALL_CHAIN |
# ifdef SAVE_CALL_CHAIN |
GC_save_callers(GC_last_stack); |
GC_save_callers(GC_last_stack); |
# endif |
# endif |
|
GC_is_full_gc = TRUE; |
if (!GC_stopped_mark(stop_func)) { |
if (!GC_stopped_mark(stop_func)) { |
if (!GC_incremental) { |
if (!GC_incremental) { |
/* We're partially done and have no way to complete or use */ |
/* We're partially done and have no way to complete or use */ |
Line 470 void GC_finish_collection() |
|
Line 486 void GC_finish_collection() |
|
# ifdef GATHERSTATS |
# ifdef GATHERSTATS |
GC_mem_found = 0; |
GC_mem_found = 0; |
# endif |
# endif |
# ifdef FIND_LEAK |
if (GC_find_leak) { |
/* Mark all objects on the free list. All objects should be */ |
/* Mark all objects on the free list. All objects should be */ |
/* marked when we're done. */ |
/* marked when we're done. */ |
{ |
{ |
Line 493 void GC_finish_collection() |
|
Line 509 void GC_finish_collection() |
|
} |
} |
} |
} |
} |
} |
/* Check that everything is marked */ |
|
GC_start_reclaim(TRUE); |
GC_start_reclaim(TRUE); |
# else |
/* The above just checks; it doesn't really reclaim anything. */ |
|
} |
|
|
GC_finalize(); |
GC_finalize(); |
# ifdef STUBBORN_ALLOC |
# ifdef STUBBORN_ALLOC |
GC_clean_changing_list(); |
GC_clean_changing_list(); |
# endif |
# endif |
|
|
# ifdef PRINTTIMES |
# ifdef PRINTTIMES |
GET_TIME(finalize_time); |
GET_TIME(finalize_time); |
# endif |
# endif |
|
|
/* Clear free list mark bits, in case they got accidentally marked */ |
/* Clear free list mark bits, in case they got accidentally marked */ |
/* Note: HBLKPTR(p) == pointer to head of block containing *p */ |
/* Note: HBLKPTR(p) == pointer to head of block containing *p */ |
/* Also subtract memory remaining from GC_mem_found count. */ |
/* (or GC_find_leak is set and they were intentionally marked.) */ |
/* Note that composite objects on free list are cleared. */ |
/* Also subtract memory remaining from GC_mem_found count. */ |
/* Thus accidentally marking a free list is not a problem; only */ |
/* Note that composite objects on free list are cleared. */ |
/* objects on the list itself will be marked, and that's fixed here. */ |
/* Thus accidentally marking a free list is not a problem; only */ |
|
/* objects on the list itself will be marked, and that's fixed here. */ |
{ |
{ |
register word size; /* current object size */ |
register word size; /* current object size */ |
register ptr_t p; /* pointer to current object */ |
register ptr_t p; /* pointer to current object */ |
Line 537 void GC_finish_collection() |
|
Line 554 void GC_finish_collection() |
|
} |
} |
|
|
|
|
# ifdef PRINTSTATS |
# ifdef PRINTSTATS |
GC_printf1("Bytes recovered before sweep - f.l. count = %ld\n", |
GC_printf1("Bytes recovered before sweep - f.l. count = %ld\n", |
(long)WORDS_TO_BYTES(GC_mem_found)); |
(long)WORDS_TO_BYTES(GC_mem_found)); |
# endif |
# endif |
|
|
/* Reconstruct free lists to contain everything not marked */ |
/* Reconstruct free lists to contain everything not marked */ |
GC_start_reclaim(FALSE); |
GC_start_reclaim(FALSE); |
|
if (GC_is_full_gc) { |
# endif /* !FIND_LEAK */ |
GC_used_heap_size_after_full = USED_HEAP_SIZE; |
|
GC_need_full_gc = FALSE; |
|
} else { |
|
GC_need_full_gc = |
|
BYTES_TO_WORDS(USED_HEAP_SIZE - GC_used_heap_size_after_full) |
|
> min_words_allocd(); |
|
} |
|
|
# ifdef PRINTSTATS |
# ifdef PRINTSTATS |
GC_printf2( |
GC_printf2( |
"Immediately reclaimed %ld bytes in heap of size %lu bytes\n", |
"Immediately reclaimed %ld bytes in heap of size %lu bytes", |
(long)WORDS_TO_BYTES(GC_mem_found), |
(long)WORDS_TO_BYTES(GC_mem_found), |
(unsigned long)GC_heapsize); |
(unsigned long)GC_heapsize); |
GC_printf2("%lu (atomic) + %lu (composite) collectable bytes in use\n", |
# ifdef USE_MUNMAP |
(unsigned long)WORDS_TO_BYTES(GC_atomic_in_use), |
GC_printf1("(%lu unmapped)", GC_unmapped_bytes); |
(unsigned long)WORDS_TO_BYTES(GC_composite_in_use)); |
# endif |
|
GC_printf2( |
|
"\n%lu (atomic) + %lu (composite) collectable bytes in use\n", |
|
(unsigned long)WORDS_TO_BYTES(GC_atomic_in_use), |
|
(unsigned long)WORDS_TO_BYTES(GC_composite_in_use)); |
# endif |
# endif |
|
|
GC_n_attempts = 0; |
GC_n_attempts = 0; |
|
GC_is_full_gc = FALSE; |
/* Reset or increment counters for next cycle */ |
/* Reset or increment counters for next cycle */ |
GC_words_allocd_before_gc += GC_words_allocd; |
GC_words_allocd_before_gc += GC_words_allocd; |
GC_non_gc_bytes_at_gc = GC_non_gc_bytes; |
GC_non_gc_bytes_at_gc = GC_non_gc_bytes; |
Line 565 void GC_finish_collection() |
|
Line 592 void GC_finish_collection() |
|
GC_words_wasted = 0; |
GC_words_wasted = 0; |
GC_mem_freed = 0; |
GC_mem_freed = 0; |
|
|
|
# ifdef USE_MUNMAP |
|
GC_unmap_old(); |
|
# endif |
# ifdef PRINTTIMES |
# ifdef PRINTTIMES |
GET_TIME(done_time); |
GET_TIME(done_time); |
GC_printf2("Finalize + initiate sweep took %lu + %lu msecs\n", |
GC_printf2("Finalize + initiate sweep took %lu + %lu msecs\n", |
Line 608 void GC_gcollect GC_PROTO(()) |
|
Line 638 void GC_gcollect GC_PROTO(()) |
|
word GC_n_heap_sects = 0; /* Number of sections currently in heap. */ |
word GC_n_heap_sects = 0; /* Number of sections currently in heap. */ |
|
|
/* |
/* |
* Use the chunk of memory starting at p of syze bytes as part of the heap. |
* Use the chunk of memory starting at p of size bytes as part of the heap. |
* Assumes p is HBLKSIZE aligned, and bytes is a multiple of HBLKSIZE. |
* Assumes p is HBLKSIZE aligned, and bytes is a multiple of HBLKSIZE. |
*/ |
*/ |
void GC_add_to_heap(p, bytes) |
void GC_add_to_heap(p, bytes) |
|
|
word bytes; |
word bytes; |
{ |
{ |
word words; |
word words; |
|
hdr * phdr; |
|
|
if (GC_n_heap_sects >= MAX_HEAP_SECTS) { |
if (GC_n_heap_sects >= MAX_HEAP_SECTS) { |
ABORT("Too many heap sections: Increase MAXHINCR or MAX_HEAP_SECTS"); |
ABORT("Too many heap sections: Increase MAXHINCR or MAX_HEAP_SECTS"); |
} |
} |
if (!GC_install_header(p)) { |
phdr = GC_install_header(p); |
|
if (0 == phdr) { |
/* This is extremely unlikely. Can't add it. This will */ |
/* This is extremely unlikely. Can't add it. This will */ |
/* almost certainly result in a 0 return from the allocator, */ |
/* almost certainly result in a 0 return from the allocator, */ |
/* which is entirely appropriate. */ |
/* which is entirely appropriate. */ |
|
|
GC_heap_sects[GC_n_heap_sects].hs_bytes = bytes; |
GC_heap_sects[GC_n_heap_sects].hs_bytes = bytes; |
GC_n_heap_sects++; |
GC_n_heap_sects++; |
words = BYTES_TO_WORDS(bytes - HDR_BYTES); |
words = BYTES_TO_WORDS(bytes - HDR_BYTES); |
HDR(p) -> hb_sz = words; |
phdr -> hb_sz = words; |
|
phdr -> hb_map = (char *)1; /* A value != GC_invalid_map */ |
|
phdr -> hb_flags = 0; |
GC_freehblk(p); |
GC_freehblk(p); |
GC_heapsize += bytes; |
GC_heapsize += bytes; |
if ((ptr_t)p <= GC_least_plausible_heap_addr |
if ((ptr_t)p <= GC_least_plausible_heap_addr |
|
|
} |
} |
} |
} |
|
|
#ifdef PRESERVE_LAST |
|
|
|
GC_bool GC_protect_last_block = FALSE; |
|
|
|
GC_bool GC_in_last_heap_sect(p) |
|
ptr_t p; |
|
{ |
|
struct HeapSect * last_heap_sect; |
|
ptr_t start; |
|
ptr_t end; |
|
|
|
if (!GC_protect_last_block) return FALSE; |
|
last_heap_sect = &(GC_heap_sects[GC_n_heap_sects-1]); |
|
start = last_heap_sect -> hs_start; |
|
if (p < start) return FALSE; |
|
end = start + last_heap_sect -> hs_bytes; |
|
if (p >= end) return FALSE; |
|
return TRUE; |
|
} |
|
#endif |
|
|
|
# if !defined(NO_DEBUGGING) |
# if !defined(NO_DEBUGGING) |
void GC_print_heap_sects() |
void GC_print_heap_sects() |
{ |
{ |
|
|
LOCK(); |
LOCK(); |
if (!GC_is_initialized) GC_init_inner(); |
if (!GC_is_initialized) GC_init_inner(); |
result = (int)GC_expand_hp_inner(divHBLKSZ((word)bytes)); |
result = (int)GC_expand_hp_inner(divHBLKSZ((word)bytes)); |
# ifdef PRESERVE_LAST |
if (result) GC_requested_heapsize += bytes; |
if (result) GC_protect_last_block = FALSE; |
|
# endif |
|
UNLOCK(); |
UNLOCK(); |
ENABLE_SIGNALS(); |
ENABLE_SIGNALS(); |
return(result); |
return(result); |
Line 813 GC_bool GC_collect_or_expand(needed_blocks, ignore_off |
|
Line 824 GC_bool GC_collect_or_expand(needed_blocks, ignore_off |
|
word needed_blocks; |
word needed_blocks; |
GC_bool ignore_off_page; |
GC_bool ignore_off_page; |
{ |
{ |
|
if (!GC_incremental && !GC_dont_gc && |
if (!GC_incremental && !GC_dont_gc && GC_should_collect()) { |
(GC_dont_expand && GC_words_allocd > 0 || GC_should_collect())) { |
GC_notify_full_gc(); |
GC_notify_full_gc(); |
GC_gcollect_inner(); |
GC_gcollect_inner(); |
} else { |
} else { |
Line 852 GC_bool ignore_off_page; |
|
Line 863 GC_bool ignore_off_page; |
|
GC_printf0("Memory available again ...\n"); |
GC_printf0("Memory available again ...\n"); |
} |
} |
# endif |
# endif |
# ifdef PRESERVE_LAST |
|
if (needed_blocks > 1) GC_protect_last_block = TRUE; |
|
/* We were forced to expand the heap as the result */ |
|
/* of a large block allocation. Avoid breaking up */ |
|
/* new block into small pieces. */ |
|
# endif |
|
} |
} |
} |
} |
return(TRUE); |
return(TRUE); |