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:
parent
88ebc7ca77
commit
6a12d967ef
89
gc.c
89
gc.c
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
if (gc_stats.collections > 0)
|
||||||
|
{
|
||||||
const double total_time = gc_stats.total_ns / 1.0e9;
|
const double total_time = gc_stats.total_ns / 1.0e9;
|
||||||
const double max_time = gc_stats.max_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,
|
||||||
|
gc_stats.collections,
|
||||||
total_time,
|
total_time,
|
||||||
(gc_stats.total_freed / total_time) / (1024*1024),
|
(gc_stats.total_freed / total_time) / (1024*1024));
|
||||||
gc_stats.collections);
|
|
||||||
|
|
||||||
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
3
gc.h
|
|
@ -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: */
|
||||||
|
|
|
||||||
21
rosella.c
21
rosella.c
|
|
@ -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))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue