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_range_end;
/* A convenient shorthand */
#define gc_other_range() (1-gc_current_range)
static value_t gc_weak_box_list;
static value_t gc_will_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_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.total_ns = 0;
gc_stats.total_freed = 0;
gc_stats.high_water = 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. */
@ -552,7 +560,7 @@ static size_t transfer_children(object_t *obj)
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_range_end = gc_free_ptr + gc_soft_limit;
}
@ -721,6 +729,13 @@ static void update_soft_limit(size_t min_free)
#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)
{
if (gc_enabled)
@ -772,7 +787,7 @@ static void _collect_garbage(size_t min_free)
#ifndef NDEBUG
/* 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
//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)
{
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)
{
const double total_time = gc_stats.total_ns / 1.0e9;
const double max_time = gc_stats.max_ns / 1.0e9;
if (gc_stats.collections > 0)
{
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",
gc_stats.total_freed,
total_time,
(gc_stats.total_freed / total_time) / (1024*1024),
gc_stats.collections);
fprintf(f, "GC: %lld bytes freed by %d GCs in %0.6f sec => %0.3f MB/sec.\n",
gc_stats.total_freed,
gc_stats.collections,
total_time,
(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",
max_time, (total_time / gc_stats.collections), gc_stats.high_water);
fprintf(f, "GC: Avg. time was %0.6f sec, max %0.6f.\n",
(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: */

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 clear_gc_stats(void);
void register_gc_root(gc_root_t *root, value_t v);
void unregister_gc_root(gc_root_t *root);
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);
/* To be provided by the main application */
void out_of_memory(void) __attribute__ ((noreturn));
void out_of_memory(void);
#endif
/* vim:set sw=2 expandtab: */

View File

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