فهرست منبع

first implementation of generational GC

Roberto Ierusalimschy 21 سال پیش
والد
کامیت
32d4f304db
7فایلهای تغییر یافته به همراه162 افزوده شده و 159 حذف شده
  1. 129 128
      lgc.c
  2. 9 8
      lgc.h
  3. 6 4
      lstate.c
  4. 4 3
      lstate.h
  5. 3 8
      lstring.c
  6. 1 2
      lstring.h
  7. 10 6
      ltests.c

+ 129 - 128
lgc.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lgc.c,v 2.7 2004/04/30 20:13:38 roberto Exp roberto $
+** $Id: lgc.c,v 2.8 2004/08/10 19:17:23 roberto Exp roberto $
 ** Garbage Collector
 ** See Copyright Notice in lua.h
 */
@@ -25,10 +25,11 @@
 
 #define GCSTEPSIZE	(40*sizeof(TValue))
 #define STEPMUL		2
-#define GCFREECOST	(sizeof(TValue)/10)
-#define GCSWEEPCOST	(sizeof(TValue)/20)
+#define GCSWEEPMAX	40
+#define GCSWEEPCOST	1
 #define GCFINALIZECOST	(10*sizeof(TValue))
-#define WAITNEXTCYCLE	(10 * GCSTEPSIZE)
+#define WAITNEXTCYCLE	(40 * GCSTEPSIZE)
+#define WAITNEXTCYCLEGN	(200 * GCSTEPSIZE)
 
 
 #define FIXEDMASK	bitmask(FIXEDBIT)
@@ -269,60 +270,61 @@ static void traversestack (global_State *g, lua_State *l) {
 
 
 /*
-** traverse a given `quantity' of gray objects,
-** turning them to black. Returns extra `quantity' traversed.
+** traverse one gray object, turning it to black.
+** Returns `quantity' traversed.
 */
-static l_mem propagatemarks (global_State *g, l_mem lim) {
-  GCObject *o;
-  while ((o = g->gray) != NULL) {
-    lua_assert(isgray(o));
-    gray2black(o);
-    switch (o->gch.tt) {
-      case LUA_TTABLE: {
-        Table *h = gco2h(o);
-        g->gray = h->gclist;
-        if (traversetable(g, h))  /* table is weak? */
-          black2gray(o);  /* keep it gray */
-        lim -= sizeof(Table) + sizeof(TValue) * h->sizearray +
-                               sizeof(Node) * sizenode(h);
-        break;
-      }
-      case LUA_TFUNCTION: {
-        Closure *cl = gco2cl(o);
-        g->gray = cl->c.gclist;
-        traverseclosure(g, cl);
-        lim -= (cl->c.isC) ? sizeCclosure(cl->c.nupvalues) :
-                             sizeLclosure(cl->l.nupvalues);
-        break;
-      }
-      case LUA_TTHREAD: {
-        lua_State *th = gco2th(o);
-        g->gray = th->gclist;
-        th->gclist = g->grayagain;
-        g->grayagain = o;
-        black2gray(o);
-        traversestack(g, th);
-        lim -= sizeof(lua_State) + sizeof(TValue) * th->stacksize +
-                                   sizeof(CallInfo) * th->size_ci;
-        break;
-      }
-      case LUA_TPROTO: {
-        Proto *p = gco2p(o);
-        g->gray = p->gclist;
-        traverseproto(g, p);
-        lim -= sizeof(Proto) + sizeof(Instruction) * p->sizecode +
-                               sizeof(Proto *) * p->sizep +
-                               sizeof(TValue) * p->sizek + 
-                               sizeof(int) * p->sizelineinfo +
-                               sizeof(LocVar) * p->sizelocvars +
-                               sizeof(TString *) * p->sizeupvalues;
-        break;
-      }
-      default: lua_assert(0);
+static l_mem propagatemark (global_State *g) {
+  GCObject *o = g->gray;
+  lua_assert(isgray(o));
+  gray2black(o);
+  switch (o->gch.tt) {
+    case LUA_TTABLE: {
+      Table *h = gco2h(o);
+      g->gray = h->gclist;
+      if (traversetable(g, h))  /* table is weak? */
+        black2gray(o);  /* keep it gray */
+      return sizeof(Table) + sizeof(TValue) * h->sizearray +
+                             sizeof(Node) * sizenode(h);
+      break;
+    }
+    case LUA_TFUNCTION: {
+      Closure *cl = gco2cl(o);
+      g->gray = cl->c.gclist;
+      traverseclosure(g, cl);
+      return (cl->c.isC) ? sizeCclosure(cl->c.nupvalues) :
+                           sizeLclosure(cl->l.nupvalues);
+      break;
     }
-    if (lim <= 0) return lim;
+    case LUA_TTHREAD: {
+      lua_State *th = gco2th(o);
+      g->gray = th->gclist;
+      th->gclist = g->grayagain;
+      g->grayagain = o;
+      black2gray(o);
+      traversestack(g, th);
+      return sizeof(lua_State) + sizeof(TValue) * th->stacksize +
+                                 sizeof(CallInfo) * th->size_ci;
+      break;
+    }
+    case LUA_TPROTO: {
+      Proto *p = gco2p(o);
+      g->gray = p->gclist;
+      traverseproto(g, p);
+      return sizeof(Proto) + sizeof(Instruction) * p->sizecode +
+                             sizeof(Proto *) * p->sizep +
+                             sizeof(TValue) * p->sizek + 
+                             sizeof(int) * p->sizelineinfo +
+                             sizeof(LocVar) * p->sizelocvars +
+                             sizeof(TString *) * p->sizeupvalues;
+      break;
+    }
+    default: lua_assert(0); return 0;
   }
-  return lim;
+}
+
+
+static void propagateall (global_State *g) {
+  while (g->gray) propagatemark(g);
 }
 
 
@@ -384,6 +386,7 @@ static void freeobj (lua_State *L, GCObject *o) {
       break;
     }
     case LUA_TSTRING: {
+      G(L)->strt.nuse--;
       luaM_free(L, o, sizestring(gco2ts(o)->len));
       break;
     }
@@ -396,59 +399,50 @@ static void freeobj (lua_State *L, GCObject *o) {
 }
 
 
-static l_mem sweepwholelist (lua_State *L, GCObject **p, int keepfixed,
-                             lu_int32 *count);
+
+#define sweepwholelist(L,p)	sweeplist(L,p,LUA_MAXINT32)
 
 
-static GCObject **sweeplist (lua_State *L, GCObject **p, int keepfixed,
-                             l_mem *plim, lu_int32 *count) {
+static GCObject **sweeplist (lua_State *L, GCObject **p, lu_int32 count) {
   GCObject *curr;
   global_State *g = G(L);
-  l_mem lim = *plim;
-  int deadmask = otherwhite(g);
-  if (keepfixed) deadmask |= FIXEDMASK;
+  int whitebit = otherwhite(g);
+  int deadmask = whitebit | FIXEDMASK;
   while ((curr = *p) != NULL) {
-    if (((curr->gch.marked ^ FIXEDMASK) & deadmask) != deadmask) {
-      makewhite(g, curr);
+    if ((curr->gch.marked ^ whitebit) & deadmask) {
+      lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT));
+      if (!G(L)->gcgenerational || isdead(g, curr))
+        makewhite(g, curr);
       if (curr->gch.tt == LUA_TTHREAD)
-        lim -= sweepwholelist(L, &gco2th(curr)->openupval, keepfixed, count);
+        sweepwholelist(L, &gco2th(curr)->openupval);
       p = &curr->gch.next;
-      lim -= GCSWEEPCOST;
     }
     else {
-      lua_assert(iswhite(curr));
+      lua_assert(isdead(g, curr));
       *p = curr->gch.next;
       if (curr == g->rootgc)  /* is the first element of the list? */
         g->rootgc = curr->gch.next;  /* adjust first */
       freeobj(L, curr);
-      lim -= GCFREECOST;
-      if (count) (*count)--;
     }
-    if (lim <= 0) break;
+    if (count-- == 0) break;
   }
-  *plim = lim;
   return p;
 }
 
 
-static l_mem sweepwholelist (lua_State *L, GCObject **p, int keepfixed,
-                             lu_int32 *count) {
-  l_mem lim = MAXLMEM;
-  /* empty lists are quite common here, so avoid useless calls */
-  if (*p) sweeplist(L, p, keepfixed, &lim, count);
-  return MAXLMEM - lim;
+static void sweepstrings (lua_State *L) {
+  global_State *g = G(L);
+  sweepwholelist(L, &G(L)->strt.hash[g->sweepstrgc++]);
 }
 
 
-static l_mem sweepstrings (lua_State *L, int keepfixed, l_mem lim) {
-  int i;
-  global_State *g = G(L);
-  for (i = g->sweepstrgc; i < g->strt.size; i++) {  /* for each list */
-    lim -= sweepwholelist(L, &G(L)->strt.hash[i], keepfixed, &g->strt.nuse);
-    if (lim <= 0) break;
+static void freelist (lua_State *L, GCObject **p) {
+  while (*p) {
+    GCObject *curr = *p;
+    *p = (*p)->gch.next;
+    if (curr != obj2gco(L))
+      freeobj(L, curr);
   }
-  g->sweepstrgc = i+1;
-  return lim;
 }
 
 
@@ -497,19 +491,12 @@ void luaC_callGCTM (lua_State *L) {
 }
 
 
-void luaC_sweepall (lua_State *L) {
+void luaC_freeall (lua_State *L) {
   global_State *g = G(L);
-  l_mem dummy = MAXLMEM;
-  /* finish (occasional) current sweep */
-  sweepstrings(L, 0, MAXLMEM);
-  sweeplist(L, &g->rootgc, 0, &dummy, NULL);
-  /* do a whole new sweep */
-  markobject(g, g->mainthread);  /* cannot collect main thread */
-  g->currentwhite = otherwhite(g);
-  g->sweepgc = &g->rootgc;
-  g->sweepstrgc = 0;
-  sweepstrings(L, 0, MAXLMEM);
-  sweeplist(L, &g->rootgc, 0, &dummy, NULL);
+  int i;
+  freelist(L, &g->rootgc);
+  for (i = 0; i < g->strt.size; i++)  /* free all string lists */
+    freelist(L, &G(L)->strt.hash[i]);
 }
 
 
@@ -553,64 +540,68 @@ static void atomic (lua_State *L) {
   g->weak = NULL;
   lua_assert(!iswhite(obj2gco(g->mainthread)));
   markobject(g, L);  /* mark running thread */
-  propagatemarks(g, MAXLMEM);
+  propagateall(g);
   /* remark gray again */
   g->gray = g->grayagain;
   g->grayagain = NULL;
-  propagatemarks(g, MAXLMEM);
+  propagateall(g);
   luaC_separateudata(L, 0);  /* separate userdata to be preserved */
   marktmu(g);  /* mark `preserved' userdata */
-  propagatemarks(g, MAXLMEM);  /* remark, to propagate `preserveness' */
+  propagateall(g);  /* remark, to propagate `preserveness' */
   cleartable(g->weak);  /* remove collected objects from weak tables */
   /* flip current white */
   g->currentwhite = otherwhite(g);
+  g->sweepstrgc = 0;
+  g->sweepgc = &g->rootgc;
   g->gcstate = GCSsweepstring;
+  if (g->gcgenerational++ > 20) g->gcgenerational = 0;
 }
 
 
-static l_mem singlestep (lua_State *L, l_mem lim) {
+static l_mem singlestep (lua_State *L) {
   global_State *g = G(L);
+  /*lua_checkmemory(L);*/
   switch (g->gcstate) {
     case GCSpropagate: {
       if (g->gray)
-        lim = propagatemarks(g, lim);
+        return propagatemark(g);
       else {  /* no more `gray' objects */
         atomic(L);  /* finish mark phase */
-        lim = 0;
+        return 0;
       }
-      break;
     }
     case GCSsweepstring: {
-      lim = sweepstrings(L, 1, lim);
-      if (g->sweepstrgc >= g->strt.size) {  /* nothing more to sweep? */
-        g->sweepstrgc = 0;
+      sweepstrings(L);
+      if (g->sweepstrgc >= g->strt.size)  /* nothing more to sweep? */
         g->gcstate = GCSsweep;  /* end sweep-string phase */
-      }
-      break;
+      return GCSWEEPCOST;
     }
     case GCSsweep: {
-      g->sweepgc = sweeplist(L, g->sweepgc, 1, &lim, NULL);
+      g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX);
       if (*g->sweepgc == NULL) {  /* nothing more to sweep? */
         checkSizes(L);
-        g->sweepgc = &g->rootgc;
         g->gcstate = GCSfinalize;  /* end sweep phase */
       }
-      break;
+      return GCSWEEPCOST;
     }
     case GCSfinalize: {
       if (g->tmudata) {
         GCTM(L);
-        lim -= GCFINALIZECOST;
+        return GCFINALIZECOST;
       }
       else {  /* no more `udata' to finalize */
-        markroot(L);  /* may restart collection */
-        lim = 0;
+        if (g->gcgenerational) {
+          atomic(L);
+          return WAITNEXTCYCLEGN;
+        }
+        else {
+          markroot(L);  /* may restart collection */
+          return WAITNEXTCYCLE;
+        }
       }
-      break;
     }
-    default: lua_assert(0);
+    default: lua_assert(0); return 0;
   }
-  return lim;
 }
 
 
@@ -618,11 +609,7 @@ void luaC_step (lua_State *L) {
   global_State *g = G(L);
   l_mem lim = (g->nblocks - (g->GCthreshold - GCSTEPSIZE)) * STEPMUL;
   do {
-    lim = singlestep(L, lim);
-    if (g->gcstate == GCSfinalize && g->tmudata == NULL) {
-      lim = -WAITNEXTCYCLE;
-      break;  /* do not start new collection */
-    }
+    lim -= singlestep(L);
   } while (lim > 0);
   g->GCthreshold = g->nblocks + GCSTEPSIZE - lim/STEPMUL;
   lua_assert((long)g->nblocks + (long)GCSTEPSIZE >= lim/STEPMUL);
@@ -631,12 +618,26 @@ void luaC_step (lua_State *L) {
 
 void luaC_fullgc (lua_State *L) {
   global_State *g = G(L);
+  if (g->gcstate == GCSpropagate || g->gcgenerational) {
+    g->gcgenerational = 0;
+    /* reset sweep marks to sweep all elements (returning them to white) */
+    g->sweepstrgc = 0;
+    g->sweepgc = &g->rootgc;
+    /* reset other collector lists */
+    g->gray = NULL;
+    g->grayagain = NULL;
+    g->weak = NULL;
+    g->gcstate = GCSsweepstring;
+  }
+  /* finish any pending sweep phase */
   while (g->gcstate != GCSfinalize) {
-    singlestep(L, MAXLMEM);
+    singlestep(L);
   }
   markroot(L);
+  lua_assert(!g->gcgenerational);
   while (g->gcstate != GCSfinalize) {
-    singlestep(L, MAXLMEM);
+    singlestep(L);
+    g->gcgenerational = 0;  /* keep it in this mode */
   }
   g->GCthreshold = g->nblocks + GCSTEPSIZE;
   luaC_callGCTM(L);  /* call finalizers */
@@ -646,7 +647,7 @@ void luaC_fullgc (lua_State *L) {
 void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) {
   global_State *g = G(L);
   lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));
-  lua_assert(g->gcstate != GCSfinalize);
+  lua_assert(g->gcgenerational || g->gcstate != GCSfinalize);
   if (g->gcstate != GCSpropagate)  /* sweeping phases? */
     black2gray(o);  /* just mark as gray to avoid other barriers */
   else  /* breaking invariant! */
@@ -657,7 +658,7 @@ void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) {
 void luaC_barrierback (lua_State *L, GCObject *o, GCObject *v) {
   global_State *g = G(L);
   lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));
-  lua_assert(g->gcstate != GCSfinalize);
+  lua_assert(g->gcgenerational || g->gcstate != GCSfinalize);
   black2gray(o);  /* make table gray (again) */
   gco2h(o)->gclist = g->grayagain;
   g->grayagain = o;
@@ -679,7 +680,7 @@ void luaC_linkupval (lua_State *L, UpVal *uv) {
   o->gch.next = g->rootgc;  /* link upvalue into `rootgc' list */
   g->rootgc = o;
   if (isgray(o)) { 
-    if (g->gcstate == GCSpropagate) {
+    if (g->gcstate == GCSpropagate || g->gcgenerational) {
       gray2black(o);  /* closed upvalues need barrier */
       luaC_barrier(L, uv, uv->v);
     }

+ 9 - 8
lgc.h

@@ -1,5 +1,5 @@
 /*
-** $Id: lgc.h,v 2.5 2004/03/15 21:04:33 roberto Exp roberto $
+** $Id: lgc.h,v 2.6 2004/08/10 19:17:23 roberto Exp roberto $
 ** Garbage Collector
 ** See Copyright Notice in lua.h
 */
@@ -39,12 +39,13 @@
 
 /*
 ** Layout for bit use in `marked' field:
-** bit 0 - object is gray
-** bit 1 - object is black
-** bit 2 - For userdata: is finalized;
-           for tables: has weak keys
-** bit 3 - for tables: has weak values
-** bit 4 - object is fixed (should not be collected)
+** bit 0 - object is white (type 0)
+** bit 1 - object is white (type 1)
+** bit 2 - object is black
+** bit 3 - for userdata: has been finalized
+** bit 3 - for tables: has weak keys
+** bit 4 - for tables: has weak values
+** bit 5 - object is fixed (should not be collected)
 */
 
 #define WHITE0BIT	0
@@ -86,7 +87,7 @@
 
 size_t luaC_separateudata (lua_State *L, int all);
 void luaC_callGCTM (lua_State *L);
-void luaC_sweepall (lua_State *L);
+void luaC_freeall (lua_State *L);
 void luaC_step (lua_State *L);
 void luaC_fullgc (lua_State *L);
 void luaC_link (lua_State *L, GCObject *o, lu_byte tt);

+ 6 - 4
lstate.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lstate.c,v 2.9 2004/06/17 14:06:52 roberto Exp roberto $
+** $Id: lstate.c,v 2.10 2004/06/17 14:25:31 roberto Exp roberto $
 ** Global State
 ** See Copyright Notice in lua.h
 */
@@ -122,9 +122,10 @@ static void preinit_state (lua_State *L, global_State *g) {
 static void close_state (lua_State *L) {
   global_State *g = G(L);
   luaF_close(L, L->stack);  /* close all upvalues for this thread */
-  luaC_sweepall(L);  /* collect all elements */
-  lua_assert(g->rootgc == obj2gco(L));
-  luaS_freeall(L);
+  luaC_freeall(L);  /* collect all objects */
+  lua_assert(g->rootgc == NULL);
+  lua_assert(g->strt.nuse == 0);
+  luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size, TString *);
   luaZ_freebuffer(L, &g->buff);
   freestack(L, L);
   lua_assert(g->nblocks == sizeof(LG));
@@ -177,6 +178,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
   luaZ_initbuffer(L, &g->buff);
   g->panic = NULL;
   g->gcstate = GCSfinalize;
+  g->gcgenerational = 0;
   g->rootgc = obj2gco(L);
   g->sweepstrgc = 0;
   g->sweepgc = &g->rootgc;

+ 4 - 3
lstate.h

@@ -1,5 +1,5 @@
 /*
-** $Id: lstate.h,v 2.4 2004/05/31 18:51:50 roberto Exp roberto $
+** $Id: lstate.h,v 2.5 2004/06/02 19:07:55 roberto Exp roberto $
 ** Global State
 ** See Copyright Notice in lua.h
 */
@@ -69,7 +69,9 @@ typedef struct global_State {
   stringtable strt;  /* hash table for strings */
   lua_Alloc realloc;  /* function to reallocate memory */
   void *ud;         /* auxiliary data to `realloc' */
-  int currentwhite;
+  lu_byte currentwhite;
+  lu_byte gcstate;  /* state of garbage collector */
+  lu_byte gcgenerational;
   GCObject *rootgc;  /* list of all collectable objects */
   GCObject *firstudata;   /* udata go to the end of `rootgc' */
   GCObject **sweepgc;  /* position of sweep in `rootgc' */
@@ -78,7 +80,6 @@ typedef struct global_State {
   GCObject *grayagain;  /* list of objects to be traversed atomically */
   GCObject *weak;  /* list of weak tables (to be cleared) */
   GCObject *tmudata;  /* list of userdata to be GC */
-  int gcstate;  /* state of garbage collector */
   Mbuffer buff;  /* temporary buffer for string concatentation */
   lu_mem GCthreshold;
   lu_mem nblocks;  /* number of `bytes' currently allocated */

+ 3 - 8
lstring.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lstring.c,v 2.1 2003/12/10 12:13:36 roberto Exp roberto $
+** $Id: lstring.c,v 2.2 2004/04/30 20:13:38 roberto Exp roberto $
 ** String table (keeps all strings handled by Lua)
 ** See Copyright Notice in lua.h
 */
@@ -19,17 +19,12 @@
 
 
 
-void luaS_freeall (lua_State *L) {
-  lua_assert(G(L)->strt.nuse==0);
-  luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size, TString *);
-}
-
-
 void luaS_resize (lua_State *L, int newsize) {
   GCObject **newhash;
   stringtable *tb;
   int i;
-  if (G(L)->sweepstrgc > 0) return;  /* cannot resize during GC traverse */
+  if (G(L)->gcstate == GCSsweepstring)
+    return;  /* cannot resize during GC traverse */
   newhash = luaM_newvector(L, newsize, GCObject *);
   tb = &G(L)->strt;
   for (i=0; i<newsize; i++) newhash[i] = NULL;

+ 1 - 2
lstring.h

@@ -1,5 +1,5 @@
 /*
-** $Id: lstring.h,v 1.37 2002/08/16 14:45:55 roberto Exp roberto $
+** $Id: lstring.h,v 1.38 2003/11/17 19:50:05 roberto Exp roberto $
 ** String table (keep all strings handled by Lua)
 ** See Copyright Notice in lua.h
 */
@@ -26,7 +26,6 @@
 
 void luaS_resize (lua_State *L, int newsize);
 Udata *luaS_newudata (lua_State *L, size_t s);
-void luaS_freeall (lua_State *L);
 TString *luaS_newlstr (lua_State *L, const char *str, size_t l);
 
 

+ 10 - 6
ltests.c

@@ -1,5 +1,5 @@
 /*
-** $Id: ltests.c,v 2.9 2004/06/02 19:08:52 roberto Exp roberto $
+** $Id: ltests.c,v 2.10 2004/07/09 18:23:17 roberto Exp roberto $
 ** Internal Module for Debugging of the Lua Implementation
 ** See Copyright Notice in lua.h
 */
@@ -153,9 +153,9 @@ void *debug_realloc (void *ud, void *block, size_t oldsize, size_t size) {
 
 static int testobjref1 (global_State *g, GCObject *f, GCObject *t) {
   if (isdead(g,t)) return 0;
-  if (g->gcstate == GCSpropagate)
+  if (g->gcstate == GCSpropagate || g->gcgenerational)
     return !isblack(f) || !iswhite(t);
-  else if (g->gcstate == GCSfinalize)
+  else if (g->gcstate == GCSfinalize && !g->gcgenerational)
     return iswhite(f);
   else
     return 1;
@@ -175,7 +175,8 @@ static void printobj (global_State *g, GCObject *o) {
 static int testobjref (global_State *g, GCObject *f, GCObject *t) {
   int r = testobjref1(g,f,t);
   if (!r) {
-    printf("%d(%02X) - ", g->gcstate, g->currentwhite);
+    printf("%d(%02X) %c - ", g->gcstate, g->currentwhite,
+                             g->gcgenerational ? 'G' : ' ');
     printobj(g, f);
     printf("\t-> ");
     printobj(g, t);
@@ -290,9 +291,12 @@ static void checkstack (global_State *g, lua_State *L1) {
 
 static void checkobject (global_State *g, GCObject *o) {
   if (isdead(g, o))
-    lua_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep);
+/*    lua_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep);*/
+{ if (!(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep))
+printf(">>> %d  %s  %02x\n", g->gcstate, luaT_typenames[o->gch.tt], o->gch.marked);
+}
   else {
-    if (g->gcstate == GCSfinalize)
+    if (g->gcstate == GCSfinalize && !g->gcgenerational)
       lua_assert(iswhite(o));
     switch (o->gch.tt) {
       case LUA_TUPVAL: {