610 lines
18 KiB
C
610 lines
18 KiB
C
/********************************************************************
|
|
** Copyright (c) 2018-2020 Guan Wenliang
|
|
** This file is part of the Berry default interpreter.
|
|
** skiars@qq.com, https://github.com/Skiars/berry
|
|
** See Copyright Notice in the LICENSE file or at
|
|
** https://github.com/Skiars/berry/blob/master/LICENSE
|
|
********************************************************************/
|
|
#include "be_gc.h"
|
|
#include "be_vm.h"
|
|
#include "be_mem.h"
|
|
#include "be_var.h"
|
|
#include "be_vector.h"
|
|
#include "be_string.h"
|
|
#include "be_class.h"
|
|
#include "be_list.h"
|
|
#include "be_func.h"
|
|
#include "be_map.h"
|
|
#include "be_module.h"
|
|
#include "be_exec.h"
|
|
#include "be_debug.h"
|
|
|
|
#define GC_PAUSE (1 << 0) /* GC will not be executed automatically */
|
|
#define GC_HALT (1 << 1) /* GC completely stopped */
|
|
#define GC_ALLOC (1 << 2) /* GC in alloc */
|
|
|
|
#define gc_try(expr) be_assert(expr); if (expr)
|
|
#define gc_setdark_safe(o) if (o) gc_setdark(o)
|
|
|
|
#define next_threshold(gc) ((gc).usage * ((size_t)(gc).steprate + 100) / 100)
|
|
|
|
#define link_gray(vm, obj) { \
|
|
(obj)->gray = (vm)->gc.gray; \
|
|
(vm)->gc.gray = gc_object(obj); \
|
|
}
|
|
|
|
static void destruct_object(bvm *vm, bgcobject *obj);
|
|
static void free_object(bvm *vm, bgcobject *obj);
|
|
|
|
void be_gc_init(bvm *vm)
|
|
{
|
|
vm->gc.usage = sizeof(bvm);
|
|
be_gc_setsteprate(vm, 200);
|
|
be_gc_init_memory_pools(vm);
|
|
}
|
|
|
|
void be_gc_deleteall(bvm *vm)
|
|
{
|
|
bupval *uv, *uvnext;
|
|
bgcobject *node, *next;
|
|
/* halt GC and delete all objects */
|
|
vm->gc.status |= GC_HALT;
|
|
/* first: call destructor */
|
|
for (node = vm->gc.list; node; node = node->next) {
|
|
destruct_object(vm, node);
|
|
}
|
|
/* second: free objects */
|
|
for (node = vm->gc.list; node; node = next) {
|
|
next = node->next;
|
|
free_object(vm, node);
|
|
}
|
|
/* delete open upvalue list */
|
|
for (uv = vm->upvalist; uv; uv = uvnext) {
|
|
uvnext = uv->u.next;
|
|
be_free(vm, uv, sizeof(bupval));
|
|
}
|
|
}
|
|
|
|
void be_gc_setsteprate(bvm *vm, int rate)
|
|
{
|
|
be_assert(rate >= 100 && rate <= 355);
|
|
vm->gc.steprate = (bbyte)(rate - 100);
|
|
vm->gc.threshold = next_threshold(vm->gc);
|
|
}
|
|
|
|
void be_gc_setpause(bvm *vm, int pause)
|
|
{
|
|
if (pause) {
|
|
vm->gc.status |= GC_PAUSE;
|
|
} else {
|
|
vm->gc.status &= ~GC_PAUSE;
|
|
}
|
|
}
|
|
|
|
bgcobject* be_newgcobj(bvm *vm, int type, size_t size)
|
|
{
|
|
bgcobject *obj = be_malloc(vm, size);
|
|
be_gc_auto(vm);
|
|
var_settype(obj, (bbyte)type); /* mark the object type */
|
|
obj->marked = GC_WHITE; /* default gc object type is white */
|
|
obj->next = vm->gc.list; /* link to the next field */
|
|
vm->gc.list = obj; /* insert to head */
|
|
return obj;
|
|
}
|
|
|
|
bgcobject* be_gc_newstr(bvm *vm, size_t size, int islong)
|
|
{
|
|
bgcobject *obj;
|
|
if (islong) { /* creating long strings is similar to ordinary GC objects */
|
|
return be_newgcobj(vm, BE_STRING, size);
|
|
}
|
|
obj = be_malloc(vm, size);
|
|
be_gc_auto(vm);
|
|
var_settype(obj, BE_STRING); /* mark the object type to BE_STRING */
|
|
obj->marked = GC_WHITE; /* default string type is white */
|
|
return obj;
|
|
}
|
|
|
|
void be_gc_fix(bvm *vm, bgcobject *obj)
|
|
{
|
|
(void)vm;
|
|
if (!gc_isconst(obj)) {
|
|
gc_setfixed(obj);
|
|
}
|
|
}
|
|
|
|
void be_gc_unfix(bvm *vm, bgcobject *obj)
|
|
{
|
|
(void)vm;
|
|
if (!gc_isconst(obj)) {
|
|
gc_clearfixed(obj);
|
|
}
|
|
}
|
|
|
|
bbool be_gc_fix_set(bvm *vm, bgcobject *obj, bbool fix)
|
|
{
|
|
(void)vm;
|
|
bbool was_fixed = gc_isfixed(obj);
|
|
if (!gc_isconst(obj)) {
|
|
if (fix) {
|
|
gc_setfixed(obj);
|
|
} else {
|
|
gc_clearfixed(obj);
|
|
}
|
|
}
|
|
return was_fixed;
|
|
}
|
|
|
|
|
|
#if BE_USE_PERF_COUNTERS
|
|
#define GC_MARK(type) do { vm->gc_mark_##type++; } while (0);
|
|
#else
|
|
#define GC_MARK(type) do { } while (0);
|
|
#endif
|
|
|
|
static void mark_gray_reset_counters(bvm *vm) {
|
|
#if BE_USE_PERF_COUNTERS
|
|
vm->gc_mark_string = 0;
|
|
vm->gc_mark_class = 0;
|
|
vm->gc_mark_proto = 0;
|
|
vm->gc_mark_instance = 0;
|
|
vm->gc_mark_map = 0;
|
|
vm->gc_mark_list = 0;
|
|
vm->gc_mark_closure = 0;
|
|
vm->gc_mark_ntvclos = 0;
|
|
vm->gc_mark_module = 0;
|
|
vm->gc_mark_comobj = 0;
|
|
#endif
|
|
}
|
|
|
|
static void mark_gray(bvm *vm, bgcobject *obj)
|
|
{
|
|
if (obj && gc_iswhite(obj) && !gc_isconst(obj)) {
|
|
gc_setgray(obj);
|
|
be_assert(!var_isstatic(obj));
|
|
switch (var_primetype(obj)) {
|
|
case BE_STRING: gc_setdark(obj); GC_MARK(string); break; /* just set dark */
|
|
case BE_CLASS: link_gray(vm, cast_class(obj)); GC_MARK(class); break;
|
|
case BE_PROTO: link_gray(vm, cast_proto(obj)); GC_MARK(proto); break;
|
|
case BE_INSTANCE: link_gray(vm, cast_instance(obj)); GC_MARK(instance); break;
|
|
case BE_MAP: link_gray(vm, cast_map(obj)); GC_MARK(map); break;
|
|
case BE_LIST: link_gray(vm, cast_list(obj)); GC_MARK(list); break;
|
|
case BE_CLOSURE: link_gray(vm, cast_closure(obj)); GC_MARK(closure); break;
|
|
case BE_NTVCLOS: link_gray(vm, cast_ntvclos(obj)); GC_MARK(ntvclos); break;
|
|
case BE_MODULE: link_gray(vm, cast_module(obj)); GC_MARK(module); break;
|
|
case BE_COMOBJ: gc_setdark(obj); GC_MARK(comobj); break; /* just set dark */
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void mark_gray_var(bvm *vm, bvalue *value)
|
|
{
|
|
if (be_isgcobj(value)) {
|
|
mark_gray(vm, var_togc(value));
|
|
}
|
|
}
|
|
|
|
static void mark_map(bvm *vm, bgcobject *obj)
|
|
{
|
|
bmap *map = cast_map(obj);
|
|
gc_try (map != NULL) {
|
|
bmapnode *node;
|
|
bmapiter iter = be_map_iter();
|
|
vm->gc.gray = map->gray; /* remove object from gray list */
|
|
while ((node = be_map_next(map, &iter)) != NULL) {
|
|
bmapkey *key = &node->key;
|
|
bvalue *val = &node->value;
|
|
if (be_isgcobj(key)) {
|
|
mark_gray(vm, var_togc(key));
|
|
}
|
|
mark_gray_var(vm, val);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void mark_list(bvm *vm, bgcobject *obj)
|
|
{
|
|
blist *list = cast_list(obj);
|
|
gc_try (list != NULL) {
|
|
bvalue *val = be_list_data(list);
|
|
bvalue *end = be_list_end(list);
|
|
vm->gc.gray = list->gray; /* remove object from gray list */
|
|
for (; val < end; val++) {
|
|
mark_gray_var(vm, val);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void mark_proto(bvm *vm, bgcobject *obj)
|
|
{
|
|
bproto *p = cast_proto(obj);
|
|
gc_try (p != NULL) {
|
|
int count;
|
|
bvalue *k = p->ktab;
|
|
bproto **ptab = p->ptab;
|
|
vm->gc.gray = p->gray; /* remove object from gray list */
|
|
for (count = p->nconst; count--; ++k) {
|
|
mark_gray_var(vm, k);
|
|
}
|
|
for (count = p->nproto; count--; ++ptab) {
|
|
mark_gray(vm, gc_object(*ptab));
|
|
}
|
|
gc_setdark_safe(p->name);
|
|
#if BE_DEBUG_SOURCE_FILE
|
|
gc_setdark_safe(p->source);
|
|
#endif
|
|
#if BE_DEBUG_VAR_INFO
|
|
if (p->nvarinfo) {
|
|
bvarinfo *vinfo = p->varinfo;
|
|
be_assert(vinfo != NULL);
|
|
for (count = p->nvarinfo; count--; ++vinfo) {
|
|
gc_setdark_safe(vinfo->name);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void mark_closure(bvm *vm, bgcobject *obj)
|
|
{
|
|
bclosure *cl = cast_closure(obj);
|
|
gc_try (cl != NULL) {
|
|
int count = cl->nupvals;
|
|
bupval **uv = cl->upvals;
|
|
vm->gc.gray = cl->gray; /* remove object from gray list */
|
|
for (; count--; ++uv) {
|
|
if (*uv && (*uv)->refcnt) {
|
|
mark_gray_var(vm, (*uv)->value);
|
|
}
|
|
}
|
|
mark_gray(vm, gc_object(cl->proto));
|
|
}
|
|
}
|
|
|
|
static void mark_ntvclos(bvm *vm, bgcobject *obj)
|
|
{
|
|
bntvclos *f = cast_ntvclos(obj);
|
|
gc_try (f != NULL) {
|
|
int count = f->nupvals;
|
|
bupval **uv = &be_ntvclos_upval(f, 0);
|
|
vm->gc.gray = f->gray; /* remove object from gray list */
|
|
for (; count--; ++uv) {
|
|
if (*uv && (*uv)->refcnt) {
|
|
mark_gray_var(vm, (*uv)->value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void mark_class(bvm *vm, bgcobject *obj)
|
|
{
|
|
bclass *c = cast_class(obj);
|
|
gc_try (c != NULL) {
|
|
vm->gc.gray = c->gray; /* remove object from gray list */
|
|
mark_gray(vm, gc_object(be_class_name(c)));
|
|
mark_gray(vm, gc_object(be_class_members(c)));
|
|
mark_gray(vm, gc_object(be_class_super(c)));
|
|
}
|
|
}
|
|
|
|
static void mark_instance(bvm *vm, bgcobject *obj)
|
|
{
|
|
binstance *o = cast_instance(obj);
|
|
gc_try (o != NULL) {
|
|
bvalue *var = be_instance_members(o);
|
|
int nvar = be_instance_member_count(o);
|
|
vm->gc.gray = o->gray; /* remove object from gray list */
|
|
mark_gray(vm, gc_object(be_instance_class(o)));
|
|
mark_gray(vm, gc_object(be_instance_super(o)));
|
|
for (; nvar--; var++) { /* mark variables */
|
|
mark_gray_var(vm, var);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void mark_module(bvm *vm, bgcobject *obj)
|
|
{
|
|
bmodule *o = cast_module(obj);
|
|
gc_try (o != NULL) {
|
|
vm->gc.gray = o->gray; /* remove object from gray list */
|
|
mark_gray(vm, gc_object(o->table));
|
|
if (!gc_isconst(o) && gc_exmark(o) & BE_MODULE_NAME) {
|
|
mark_gray(vm, gc_object(o->info.sname));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void free_proto(bvm *vm, bgcobject *obj)
|
|
{
|
|
bproto *proto = cast_proto(obj);
|
|
gc_try (proto != NULL) {
|
|
be_free(vm, proto->upvals, proto->nupvals * sizeof(bupvaldesc));
|
|
be_free(vm, proto->ktab, proto->nconst * sizeof(bvalue));
|
|
be_free(vm, proto->ptab, proto->nproto * sizeof(bproto*));
|
|
be_free(vm, proto->code, proto->codesize * sizeof(binstruction));
|
|
#if BE_DEBUG_RUNTIME_INFO
|
|
be_free(vm, proto->lineinfo, proto->nlineinfo * sizeof(blineinfo));
|
|
#endif
|
|
#if BE_DEBUG_VAR_INFO
|
|
be_free(vm, proto->varinfo, proto->nvarinfo * sizeof(bvarinfo));
|
|
#endif
|
|
be_free(vm, proto, sizeof(bproto));
|
|
}
|
|
}
|
|
|
|
static void free_closure(bvm *vm, bgcobject *obj)
|
|
{
|
|
bclosure *cl = cast_closure(obj);
|
|
gc_try (cl != NULL) {
|
|
int count = cl->nupvals;
|
|
be_release_upvalues(vm, cl);
|
|
be_free(vm, cl, sizeof(bclosure)
|
|
+ sizeof(bupval*) * ((size_t)count - 1));
|
|
}
|
|
}
|
|
|
|
static void free_ntvclos(bvm *vm, bgcobject *obj)
|
|
{
|
|
bntvclos *f = cast_ntvclos(obj);
|
|
gc_try (f != NULL) {
|
|
int count = f->nupvals;
|
|
bupval **uv = &be_ntvclos_upval(f, 0);
|
|
while (count--) {
|
|
be_free(vm, *uv++, sizeof(bupval));
|
|
}
|
|
be_free(vm, f, sizeof(bntvclos) + sizeof(bupval*) * f->nupvals);
|
|
}
|
|
}
|
|
|
|
static void free_lstring(bvm *vm, bgcobject *obj)
|
|
{
|
|
blstring *ls = gc_cast(obj, BE_STRING, blstring);
|
|
gc_try (ls != NULL) {
|
|
be_free(vm, ls, sizeof(blstring) + ls->llen + 1);
|
|
}
|
|
}
|
|
|
|
static void free_instance(bvm *vm, bgcobject *obj)
|
|
{
|
|
binstance *o = cast_instance(obj);
|
|
int nvar = be_instance_member_count(o);
|
|
be_free(vm, obj, sizeof(binstance) + sizeof(bvalue) * (nvar - 1));
|
|
}
|
|
|
|
static void free_object(bvm *vm, bgcobject *obj)
|
|
{
|
|
switch (var_primetype(obj)) {
|
|
case BE_STRING: free_lstring(vm, obj); break; /* long string */
|
|
case BE_CLASS: be_free(vm, obj, sizeof(bclass)); break;
|
|
case BE_INSTANCE: free_instance(vm, obj); break;
|
|
case BE_MAP: be_map_delete(vm, cast_map(obj)); break;
|
|
case BE_LIST: be_list_delete(vm, cast_list(obj)); break;
|
|
case BE_CLOSURE: free_closure(vm, obj); break;
|
|
case BE_NTVCLOS: free_ntvclos(vm, obj); break;
|
|
case BE_PROTO: free_proto(vm, obj); break;
|
|
case BE_MODULE: be_module_delete(vm, cast_module(obj)); break;
|
|
case BE_COMOBJ: be_commonobj_delete(vm, obj); break;
|
|
default: break; /* case BE_STRING: break; */
|
|
}
|
|
}
|
|
|
|
static void premark_internal(bvm *vm)
|
|
{
|
|
mark_gray(vm, gc_object(vm->module.loaded));
|
|
mark_gray(vm, gc_object(vm->module.path));
|
|
mark_gray(vm, gc_object(vm->ntvclass));
|
|
#if BE_USE_DEBUG_HOOK
|
|
if (be_isgcobj(&vm->hook)) {
|
|
mark_gray(vm, gc_object(var_toobj(&vm->hook)));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void premark_global(bvm *vm)
|
|
{
|
|
bvalue *v = vm->gbldesc.global.vlist.data;
|
|
bvalue *end = v + be_global_count(vm);
|
|
while (v < end) {
|
|
if (be_isgcobj(v)) {
|
|
mark_gray(vm, var_togc(v));
|
|
}
|
|
++v;
|
|
}
|
|
v = vm->gbldesc.builtin.vlist.data;
|
|
end = v + be_builtin_count(vm);
|
|
while (v < end) {
|
|
mark_gray_var(vm, v++);
|
|
}
|
|
}
|
|
|
|
static void premark_stack(bvm *vm)
|
|
{
|
|
bvalue *v = vm->stack, *end = vm->top;
|
|
/* mark live objects */
|
|
for (; v < end; ++v) {
|
|
mark_gray_var(vm, v);
|
|
}
|
|
/* set other values to nil */
|
|
end = vm->stacktop;
|
|
for (; v < end; ++v) {
|
|
var_setnil(v);
|
|
}
|
|
}
|
|
|
|
static void premark_tracestack(bvm *vm)
|
|
{
|
|
bcallsnapshot *cf = be_vector_data(&vm->tracestack);
|
|
bcallsnapshot *end = be_vector_end(&vm->tracestack);
|
|
for (; cf <= end; ++cf) {
|
|
mark_gray_var(vm, &cf->func);
|
|
}
|
|
}
|
|
|
|
static void premark_fixed(bvm *vm)
|
|
{
|
|
bgcobject *node = vm->gc.list;
|
|
for (; node; node = node->next) {
|
|
if (gc_isfixed(node) && gc_iswhite(node)) {
|
|
mark_gray(vm, node);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void mark_unscanned(bvm *vm)
|
|
{
|
|
while (vm->gc.gray) {
|
|
bgcobject *obj = vm->gc.gray;
|
|
if (obj && !gc_isdark(obj) && !gc_isconst(obj)) {
|
|
gc_setdark(obj);
|
|
be_assert(!var_isstatic(obj));
|
|
switch (var_primetype(obj)) {
|
|
case BE_CLASS: mark_class(vm, obj); break;
|
|
case BE_PROTO: mark_proto(vm, obj); break;
|
|
case BE_INSTANCE: mark_instance(vm, obj); break;
|
|
case BE_MAP: mark_map(vm, obj); break;
|
|
case BE_LIST: mark_list(vm, obj); break;
|
|
case BE_CLOSURE: mark_closure(vm, obj); break;
|
|
case BE_NTVCLOS: mark_ntvclos(vm, obj); break;
|
|
case BE_MODULE: mark_module(vm, obj); break;
|
|
default:
|
|
be_assert(0); /* error */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void destruct_object(bvm *vm, bgcobject *obj)
|
|
{
|
|
if (vm->gc.status & GC_ALLOC) {
|
|
return; /* no destructor is called during the allocation. */
|
|
}
|
|
if (obj->type == BE_INSTANCE) {
|
|
int type;
|
|
binstance *ins = cast_instance(obj);
|
|
/* does not GC when creating the string "deinit". */
|
|
type = be_instance_member_simple(vm, ins, str_literal(vm, "deinit"), vm->top);
|
|
be_incrtop(vm);
|
|
if (basetype(type) == BE_FUNCTION) {
|
|
var_setinstance(vm->top, ins); /* push instance on stack as arg 1 */
|
|
be_incrtop(vm);
|
|
be_dofunc(vm, vm->top - 2, 1); /* warning, there shoudln't be any exception raised here, or the gc stops */
|
|
be_stackpop(vm, 1);
|
|
}
|
|
be_stackpop(vm, 1);
|
|
}
|
|
}
|
|
|
|
static void destruct_white(bvm *vm)
|
|
{
|
|
bgcobject *node = vm->gc.list;
|
|
/* since the destructor may allocate objects, we must first suspend the GC */
|
|
vm->gc.status |= GC_HALT; /* mark GC is halt */
|
|
while (node) {
|
|
if (gc_iswhite(node)) {
|
|
destruct_object(vm, node);
|
|
}
|
|
node = node->next;
|
|
}
|
|
vm->gc.status &= ~GC_HALT; /* reset GC halt flag */
|
|
}
|
|
|
|
static void delete_white(bvm *vm)
|
|
{
|
|
bgcobject *node, *prev, *next;
|
|
for (node = vm->gc.list, prev = node; node; node = next) {
|
|
next = node->next;
|
|
if (gc_iswhite(node)) {
|
|
if (node == vm->gc.list) { /* first node */
|
|
vm->gc.list = node->next;
|
|
prev = node->next;
|
|
} else { /* not first node */
|
|
prev->next = next;
|
|
}
|
|
free_object(vm, node);
|
|
#if BE_USE_PERF_COUNTERS
|
|
vm->counter_gc_freed++;
|
|
#endif
|
|
} else {
|
|
gc_setwhite(node);
|
|
prev = node;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void reset_fixedlist(bvm *vm)
|
|
{
|
|
bgcobject *node;
|
|
for (node = vm->gc.fixed; node; node = node->next) {
|
|
if (gc_isdark(node)) {
|
|
gc_setwhite(node);
|
|
}
|
|
}
|
|
}
|
|
|
|
void be_gc_auto(bvm *vm)
|
|
{
|
|
if (vm->gc.status & GC_PAUSE && (BE_USE_DEBUG_GC || vm->gc.usage > vm->gc.threshold || comp_is_gc_debug(vm))) {
|
|
be_gc_collect(vm);
|
|
}
|
|
}
|
|
|
|
size_t be_gc_memcount(bvm *vm)
|
|
{
|
|
return vm->gc.usage;
|
|
}
|
|
|
|
#if BE_USE_PERF_COUNTERS
|
|
#define GC_TIMER(i) if (vm->microsfnct) { vm->micros_gc##i = vm->microsfnct(); }
|
|
#else
|
|
#define GC_TIMER(i)
|
|
#endif
|
|
|
|
void be_gc_collect(bvm *vm)
|
|
{
|
|
if (vm->gc.status & GC_HALT) {
|
|
return; /* the GC cannot run for some reason */
|
|
}
|
|
#if BE_USE_PERF_COUNTERS
|
|
size_t slors_used_before_gc, slots_allocated_before_gc;
|
|
be_gc_memory_pools_info(vm, &slors_used_before_gc, &slots_allocated_before_gc);
|
|
vm->counter_gc_kept = 0;
|
|
vm->counter_gc_freed = 0;
|
|
#endif
|
|
if (vm->obshook != NULL) (*vm->obshook)(vm, BE_OBS_GC_START, vm->gc.usage);
|
|
GC_TIMER(0);
|
|
/* step 1: set root-set reference objects to unscanned */
|
|
mark_gray_reset_counters(vm); /* reset all internal counters */
|
|
premark_internal(vm); /* object internal the VM */
|
|
premark_global(vm); /* global objects */
|
|
premark_stack(vm); /* stack objects */
|
|
premark_tracestack(vm); /* trace stack objects */
|
|
premark_fixed(vm); /* fixed objects */
|
|
GC_TIMER(1);
|
|
/* step 2: set unscanned objects to black */
|
|
mark_unscanned(vm);
|
|
GC_TIMER(2);
|
|
/* step 3: destruct and delete unreachable objects */
|
|
destruct_white(vm);
|
|
delete_white(vm);
|
|
be_gcstrtab(vm);
|
|
GC_TIMER(3);
|
|
/* step 4: reset the fixed objects */
|
|
reset_fixedlist(vm);
|
|
GC_TIMER(4);
|
|
/* step 5: calculate the next GC threshold */
|
|
vm->gc.threshold = next_threshold(vm->gc);
|
|
be_gc_memory_pools(vm); /* free unsued memory pools */
|
|
GC_TIMER(5);
|
|
#if BE_USE_PERF_COUNTERS
|
|
size_t slors_used_after_gc, slots_allocated_after_gc;
|
|
be_gc_memory_pools_info(vm, &slors_used_after_gc, &slots_allocated_after_gc);
|
|
if (vm->obshook != NULL) (*vm->obshook)(vm, BE_OBS_GC_END, vm->gc.usage, vm->counter_gc_kept, vm->counter_gc_freed,
|
|
slors_used_before_gc, slots_allocated_before_gc,
|
|
slors_used_after_gc, slots_allocated_after_gc);
|
|
#else
|
|
if (vm->obshook != NULL) (*vm->obshook)(vm, BE_OBS_GC_END, vm->gc.usage);
|
|
#endif
|
|
}
|