If hard limit is exceeded, try to allocate more memory from the OS.

Reduce initial allocation to 1 MB now that it can by increased at runtime.
Also remove 'noreturn' attribute from the public API.
If out_of_memory() returns, GC will abort() internally.
This commit is contained in:
Jesse D. McDonald 2009-11-15 23:50:03 -06:00
parent 88ebc7ca77
commit 6a12d967ef
3 changed files with 94 additions and 27 deletions

97
gc.c
View File

@ -39,6 +39,9 @@ static int gc_current_range;
static char *gc_free_ptr; static char *gc_free_ptr;
static char *gc_range_end; static char *gc_range_end;
/* A convenient shorthand */
#define gc_other_range() (1-gc_current_range)
static value_t gc_weak_box_list; static value_t gc_weak_box_list;
static value_t gc_will_list; static value_t gc_will_list;
static value_t gc_will_active_list; static value_t gc_will_active_list;
@ -386,17 +389,22 @@ void gc_init(size_t min_size, size_t max_size)
gc_soft_limit = gc_min_size; gc_soft_limit = gc_min_size;
gc_range_end = gc_free_ptr + gc_soft_limit; gc_range_end = gc_free_ptr + gc_soft_limit;
gc_weak_box_list = NIL;
gc_will_list = NIL;
gc_will_active_list = NIL;
clear_gc_stats();
gc_enabled = true;
}
void clear_gc_stats(void)
{
gc_stats.collections = 0; gc_stats.collections = 0;
gc_stats.total_ns = 0; gc_stats.total_ns = 0;
gc_stats.total_freed = 0; gc_stats.total_freed = 0;
gc_stats.high_water = 0; gc_stats.high_water = 0;
gc_stats.max_ns = 0; gc_stats.max_ns = 0;
gc_weak_box_list = NIL;
gc_will_list = NIL;
gc_will_active_list = NIL;
gc_enabled = true;
} }
/* Preconditions: nbytes pre-aligned a la gc_align(), and space exists. */ /* Preconditions: nbytes pre-aligned a la gc_align(), and space exists. */
@ -552,7 +560,7 @@ static size_t transfer_children(object_t *obj)
static void swap_gc_ranges(void) static void swap_gc_ranges(void)
{ {
gc_current_range = 1 - gc_current_range; gc_current_range = gc_other_range();
gc_free_ptr = gc_ranges[gc_current_range]; gc_free_ptr = gc_ranges[gc_current_range];
gc_range_end = gc_free_ptr + gc_soft_limit; gc_range_end = gc_free_ptr + gc_soft_limit;
} }
@ -721,6 +729,13 @@ static void update_soft_limit(size_t min_free)
#endif #endif
} }
static void _out_of_memory(void) __attribute__ ((noreturn));
static void _out_of_memory(void)
{
out_of_memory();
abort();
}
static void _collect_garbage(size_t min_free) static void _collect_garbage(size_t min_free)
{ {
if (gc_enabled) if (gc_enabled)
@ -772,7 +787,7 @@ static void _collect_garbage(size_t min_free)
#ifndef NDEBUG #ifndef NDEBUG
/* Clear old range, to make it easier to detect bugs. */ /* Clear old range, to make it easier to detect bugs. */
memset(gc_ranges[1-gc_current_range], 0, gc_soft_limit); memset(gc_ranges[gc_other_range()], 0, gc_soft_limit);
#endif #endif
//debug(("Finished collection with %d bytes to spare (out of %d bytes).\n", gc_free_space(), gc_soft_limit)); //debug(("Finished collection with %d bytes to spare (out of %d bytes).\n", gc_free_space(), gc_soft_limit));
@ -803,7 +818,43 @@ static void _collect_garbage(size_t min_free)
if (gc_free_space() < min_free) if (gc_free_space() < min_free)
{ {
out_of_memory(); size_t bytes_used = gc_free_ptr - gc_ranges[gc_current_range];
size_t need_bytes = bytes_used + min_free;
/* If GC is disabled then we can't move anything, so reallocating is impossible. */
if (!gc_enabled)
_out_of_memory();
/*
* Try to get more memory from the C runtime.
*/
debug(("Ran out of free memory; will try to allocate more...\n"));
do {
release_assert(gc_max_size < (SIZE_MAX/2));
gc_max_size *= 2;
} while (gc_max_size < need_bytes);
/* Reallocate the unused space. */
free(gc_ranges[gc_other_range()]);
gc_ranges[gc_other_range()] = (char*)malloc(gc_max_size);
/* See if reallocation succeeded. */
if (!gc_ranges[gc_other_range()])
_out_of_memory();
/* Move everything into the newly enlarged space.
* Will update the soft-limit. */
_collect_garbage(0);
/* Reallocate the other space, now unused. */
free(gc_ranges[gc_other_range()]);
gc_ranges[gc_other_range()] = (char*)malloc(gc_max_size);
/* Ensure second reallocation succeeded. */
if (!gc_ranges[gc_other_range()])
_out_of_memory();
} }
} }
@ -976,17 +1027,27 @@ void fprint_value(FILE *f, value_t v)
void fprint_gc_stats(FILE *f) void fprint_gc_stats(FILE *f)
{ {
const double total_time = gc_stats.total_ns / 1.0e9; if (gc_stats.collections > 0)
const double max_time = gc_stats.max_ns / 1.0e9; {
const double total_time = gc_stats.total_ns / 1.0e9;
const double max_time = gc_stats.max_ns / 1.0e9;
fprintf(f, "%lld bytes freed in %0.6f sec => %0.3f MB/sec. (%d GCs.)\n", fprintf(f, "GC: %lld bytes freed by %d GCs in %0.6f sec => %0.3f MB/sec.\n",
gc_stats.total_freed, gc_stats.total_freed,
total_time, gc_stats.collections,
(gc_stats.total_freed / total_time) / (1024*1024), total_time,
gc_stats.collections); (gc_stats.total_freed / total_time) / (1024*1024));
fprintf(f, "Max GC time was %0.6f sec, avg. %0.6f sec; peak heap size was %d bytes.\n", fprintf(f, "GC: Avg. time was %0.6f sec, max %0.6f.\n",
max_time, (total_time / gc_stats.collections), gc_stats.high_water); (total_time / gc_stats.collections), max_time);
fprintf(f, "GC: The soft-limit peaked at %d bytes out of %d allocated.\n",
gc_stats.high_water, gc_max_size);
}
else
{
fputs("GC: No garbage collection was performed.\n", f);
}
} }
/* vim:set sw=2 expandtab: */ /* vim:set sw=2 expandtab: */

3
gc.h
View File

@ -376,6 +376,7 @@ static inline builtin_fn_t *_get_builtin_fn(value_t v)
} }
void gc_init(size_t min_size, size_t max_size); void gc_init(size_t min_size, size_t max_size);
void clear_gc_stats(void);
void register_gc_root(gc_root_t *root, value_t v); void register_gc_root(gc_root_t *root, value_t v);
void unregister_gc_root(gc_root_t *root); void unregister_gc_root(gc_root_t *root);
void *gc_alloc(size_t nbytes); void *gc_alloc(size_t nbytes);
@ -398,7 +399,7 @@ static inline void print_gc_stats(void)
void _release_assert(bool expr, const char *str, const char *file, int line); void _release_assert(bool expr, const char *str, const char *file, int line);
/* To be provided by the main application */ /* To be provided by the main application */
void out_of_memory(void) __attribute__ ((noreturn)); void out_of_memory(void);
#endif #endif
/* vim:set sw=2 expandtab: */ /* vim:set sw=2 expandtab: */

View File

@ -31,7 +31,7 @@ int main(int argc, char **argv)
{ {
srand((unsigned int)time(NULL)); srand((unsigned int)time(NULL));
gc_init(1024, 256*1024*1024); gc_init(256*1024, 1024*1024);
builtin_init(); builtin_init();
interpreter_init(); interpreter_init();
@ -73,6 +73,10 @@ int main(int argc, char **argv)
print_value(CAR(result)); print_value(CAR(result));
nl(); nl();
} }
nl();
fflush(stdout);
print_gc_stats();
} }
else else
{ {
@ -137,10 +141,12 @@ static void test_garbage_collection(bool keep_going)
while (1) while (1)
{ {
int r = rand() & 0xffff; int r = rand() & 0x1ffff;
if (r == 0) if (r == 0)
{
root.value = fixnum_value(rand()); root.value = fixnum_value(rand());
}
else else
{ {
switch (r & 7) switch (r & 7)
@ -173,14 +179,10 @@ static void test_garbage_collection(bool keep_going)
if (++count >= 50000000) if (++count >= 50000000)
{ {
nl();
print_gc_stats(); print_gc_stats();
nl();
gc_stats.collections = 0; clear_gc_stats();
gc_stats.total_ns = 0;
gc_stats.total_freed = 0;
gc_stats.high_water = 0;
gc_stats.max_ns = 0;
count = 0; count = 0;
if (!keep_going) if (!keep_going)
@ -196,6 +198,9 @@ static void test_reader(void)
value_t v; value_t v;
do { do {
fputs("> ", stdout);
fflush(stdout);
v = read_value(stdin); v = read_value(stdin);
if (is_struct(v) && _get_struct(v)->type == lookup_builtin(BI_LAMBDA)) if (is_struct(v) && _get_struct(v)->type == lookup_builtin(BI_LAMBDA))
{ {