commit 925fe8a0f2a667c96c015ee74d1a702af9ea5507
parent 789e7acdea3ada96bd00b7aac6d82e805bfee85c
Author: Roberto Ierusalimschy <roberto@inf.puc-rio.br>
Date: Thu, 7 Dec 2023 15:44:44 -0300
First criteria for shifts minor<->major
Diffstat:
M | lapi.c | | | 25 | ++++++++++++++----------- |
M | lbaselib.c | | | 13 | +++++++------ |
M | lgc.c | | | 81 | +++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------- |
M | lgc.h | | | 48 | ++++++++++++++++++++++++++---------------------- |
M | lstate.c | | | 3 | ++- |
M | lstate.h | | | 9 | +++++---- |
M | lua.c | | | 2 | +- |
M | manual/manual.of | | | 134 | +++++++++++++++++++++++++++++++++++++++++++++---------------------------------- |
8 files changed, 187 insertions(+), 128 deletions(-)
diff --git a/lapi.c b/lapi.c
@@ -1204,26 +1204,29 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
break;
}
case LUA_GCGEN: {
- unsigned int minormul = va_arg(argp, unsigned int);
- unsigned int majormul = va_arg(argp, unsigned int);
+ int minormul = va_arg(argp, int);
+ int minormajor = va_arg(argp, int);
+ int majorminor = va_arg(argp, int);
res = (g->gckind == KGC_INC) ? LUA_GCINC : LUA_GCGEN;
- if (minormul != 0)
+ if (minormul >= 0)
setgcparam(g, genminormul, minormul);
- if (majormul != 0)
- setgcparam(g, genmajormul, majormul);
+ if (minormajor >= 0)
+ setgcparam(g, minormajor, minormajor);
+ if (majorminor >= 0)
+ setgcparam(g, majorminor, majorminor);
luaC_changemode(L, KGC_GENMINOR);
break;
}
case LUA_GCINC: {
- unsigned int pause = va_arg(argp, unsigned int);
- unsigned int stepmul = va_arg(argp, unsigned int);
- unsigned int stepsize = va_arg(argp, unsigned int);
+ int pause = va_arg(argp, int);
+ int stepmul = va_arg(argp, int);
+ int stepsize = va_arg(argp, int);
res = (g->gckind == KGC_INC) ? LUA_GCINC : LUA_GCGEN;
- if (pause != 0)
+ if (pause >= 0)
setgcparam(g, gcpause, pause);
- if (stepmul != 0)
+ if (stepmul >= 0)
setgcparam(g, gcstepmul, stepmul);
- if (stepsize != 0)
+ if (stepsize >= 0)
g->gcstepsize = (stepsize <= log2maxs(l_obj)) ? stepsize
: log2maxs(l_obj);
luaC_changemode(L, KGC_INC);
diff --git a/lbaselib.c b/lbaselib.c
@@ -224,14 +224,15 @@ static int luaB_collectgarbage (lua_State *L) {
return 1;
}
case LUA_GCGEN: {
- int minormul = (int)luaL_optinteger(L, 2, 0);
- int majormul = (int)luaL_optinteger(L, 3, 0);
- return pushmode(L, lua_gc(L, o, minormul, majormul));
+ int minormul = (int)luaL_optinteger(L, 2, -1);
+ int majorminor = (int)luaL_optinteger(L, 3, -1);
+ int minormajor = (int)luaL_optinteger(L, 4, -1);
+ return pushmode(L, lua_gc(L, o, minormul, majorminor, minormajor));
}
case LUA_GCINC: {
- int pause = (int)luaL_optinteger(L, 2, 0);
- int stepmul = (int)luaL_optinteger(L, 3, 0);
- int stepsize = (int)luaL_optinteger(L, 4, 0);
+ int pause = (int)luaL_optinteger(L, 2, -1);
+ int stepmul = (int)luaL_optinteger(L, 3, -1);
+ int stepsize = (int)luaL_optinteger(L, 4, -1);
return pushmode(L, lua_gc(L, o, pause, stepmul, stepsize));
}
default: {
diff --git a/lgc.c b/lgc.c
@@ -1205,20 +1205,21 @@ static void correctgraylists (global_State *g) {
/*
** Mark black 'OLD1' objects when starting a new young collection.
** Gray objects are already in some gray list, and so will be visited in
-** the atomic step. The counter 'GCmajorminor' keeps how many objects to
-** become old before a major collection.
+** the atomic step. Returns the number of objects that became old.
*/
-static void markold (global_State *g, GCObject *from, GCObject *to) {
+static l_obj markold (global_State *g, GCObject *from, GCObject *to) {
GCObject *p;
+ l_obj count = 0;
for (p = from; p != to; p = p->next) {
if (getage(p) == G_OLD1) {
lua_assert(!iswhite(p));
setage(p, G_OLD); /* now they are old */
- g->GCmajorminor--; /* one more old object */
+ count++; /* one more old object */
if (isblack(p))
reallymarkobject(g, p);
}
}
+ return count;
}
@@ -1240,7 +1241,7 @@ static void finishgencycle (lua_State *L, global_State *g) {
*/
static void atomic2major (lua_State *L, global_State *g) {
l_obj stepsize = cast(l_obj, 1) << g->gcstepsize;
- g->GCmajorminor = gettotalobjs(g);
+ g->GCmajorminor = g->marked; /* number of live objects */
g->gckind = KGC_GENMAJOR;
g->reallyold = g->old1 = g->survival = NULL;
g->finobjrold = g->finobjold1 = g->finobjsur = NULL;
@@ -1250,29 +1251,53 @@ static void atomic2major (lua_State *L, global_State *g) {
/*
+** Decide whether to shift to major mode. It tests two conditions:
+** 1) Whether the number of added old objects in this collection is more
+** than half the number of new objects. ("step" is the number of objects
+** created between minor collections. Except for forward barriers, it
+** is the maximum number of objects that can become old in each minor
+** collection.)
+** 2) Whether the accumulated number of added old objects is larger
+** than 'minormajor'% of the number of lived objects after the last
+** major collection. (That percentage is computed in 'limit'.)
+*/
+static int checkminormajor (lua_State *L, global_State *g, l_obj addedold1) {
+ l_obj step = applygcparam(g, genminormul, g->GCmajorminor);
+ l_obj limit = applygcparam(g, minormajor, g->GCmajorminor);
+//printf("-> major? %ld %ld %ld %ld (%ld)\n", g->marked, limit, step, addedold1, gettotalobjs(g));
+ if (addedold1 >= (step >> 1) || g->marked >= limit) {
+ atomic2major(L, g); /* go to major mode */
+ return 1;
+ }
+ return 0; /* stay in minor mode */
+}
+
+/*
** Does a young collection. First, mark 'OLD1' objects. Then does the
** atomic step. Then, check whether to continue in minor mode. If so,
** sweep all lists and advance pointers. Finally, finish the collection.
*/
static void youngcollection (lua_State *L, global_State *g) {
+ l_obj addedold1 = 0;
+ l_obj marked = g->marked; /* preserve 'g->marked' */
GCObject **psurvival; /* to point to first non-dead survival object */
GCObject *dummy; /* dummy out parameter to 'sweepgen' */
lua_assert(g->gcstate == GCSpropagate);
- g->marked = 0;
if (g->firstold1) { /* are there regular OLD1 objects? */
- markold(g, g->firstold1, g->reallyold); /* mark them */
+ addedold1 += markold(g, g->firstold1, g->reallyold); /* mark them */
g->firstold1 = NULL; /* no more OLD1 objects (for now) */
}
- markold(g, g->finobj, g->finobjrold);
- markold(g, g->tobefnz, NULL);
+ addedold1 += markold(g, g->finobj, g->finobjrold);
+ addedold1 += markold(g, g->tobefnz, NULL);
+
+ atomic(L); /* will lose 'g->marked' */
- atomic(L);
+ /* keep total number of added old1 objects */
+ g->marked = marked + addedold1;
/* decide whether to shift to major mode */
- if (g->GCmajorminor <= 0) { /* ?? */
- atomic2major(L, g); /* go to major mode */
+ if (checkminormajor(L, g, addedold1))
return; /* nothing else to be done here */
- }
/* sweep nursery and get a pointer to its last live element */
g->gcstate = GCSswpallgc;
@@ -1319,7 +1344,8 @@ static void atomic2gen (lua_State *L, global_State *g) {
sweep2old(L, &g->tobefnz);
g->gckind = KGC_GENMINOR;
- g->GCmajorminor = applygcparam(g, genmajormul, g->marked);
+ g->GCmajorminor = g->marked; /* "base" for number of objects */
+ g->marked = 0; /* to count the number of added old1 objects */
finishgencycle(L, g);
}
@@ -1329,7 +1355,7 @@ static void atomic2gen (lua_State *L, global_State *g) {
** total number of objects grows 'genminormul'%.
*/
static void setminordebt (global_State *g) {
- luaE_setdebt(g, applygcparam(g, genminormul, gettotalobjs(g)));
+ luaE_setdebt(g, applygcparam(g, genminormul, g->GCmajorminor));
}
@@ -1369,13 +1395,11 @@ static void enterinc (global_State *g) {
*/
void luaC_changemode (lua_State *L, int newmode) {
global_State *g = G(L);
+ if (g->gckind == KGC_GENMAJOR) /* doing major collections? */
+ g->gckind = KGC_INC; /* already incremental but in name */
if (newmode != g->gckind) { /* does it need to change? */
- if (newmode == KGC_INC) { /* entering incremental mode? */
- if (g->gckind == KGC_GENMAJOR)
- g->gckind = KGC_INC; /* already incremental but in name */
- else
- enterinc(g); /* entering incremental mode */
- }
+ if (newmode == KGC_INC) /* entering incremental mode? */
+ enterinc(g); /* entering incremental mode */
else {
lua_assert(newmode == KGC_GENMINOR);
entergen(L, g);
@@ -1396,16 +1420,24 @@ static void fullgen (lua_State *L, global_State *g) {
/*
** After an atomic incremental step from a major collection,
** check whether collector could return to minor collections.
+** It checks whether the number of objects 'tobecollected'
+** is greater than 'majorminor'% of the number of objects added
+** since the last collection ('addedobjs').
*/
static int checkmajorminor (lua_State *L, global_State *g) {
if (g->gckind == KGC_GENMAJOR) {
- l_obj numobjs = gettotalobjs(g); /* current count */
- if (g->marked < numobjs - (numobjs >> 2)) { /* ?? */
+ l_obj numobjs = gettotalobjs(g);
+ l_obj addedobjs = numobjs - g->GCmajorminor;
+ l_obj limit = applygcparam(g, majorminor, addedobjs);
+ l_obj tobecollected = numobjs - g->marked;
+//printf("-> minor? %ld %ld %ld\n", tobecollected, limit, numobjs);
+ if (tobecollected > limit) {
atomic2gen(L, g); /* return to generational mode */
setminordebt(g);
return 0; /* exit incremental collection */
}
}
+ g->GCmajorminor = g->marked; /* prepare for next collection */
return 1; /* stay doing incremental collections */
}
@@ -1634,8 +1666,6 @@ void luaC_step (lua_State *L) {
if (!gcrunning(g)) /* not running? */
luaE_setdebt(g, 2000);
else {
-//printf("> step: %d %d %ld %ld -> ", g->gckind, g->gcstate, gettotalobjs(g), g->GCdebt);
-
switch (g->gckind) {
case KGC_INC: case KGC_GENMAJOR:
incstep(L, g);
@@ -1645,7 +1675,6 @@ void luaC_step (lua_State *L) {
setminordebt(g);
break;
}
-//printf("%d %d %ld %ld\n", g->gckind, g->gcstate, gettotalobjs(g), g->GCdebt);
}
}
diff --git a/lgc.h b/lgc.h
@@ -161,10 +161,24 @@
/* Default Values for GC parameters */
-/* generational */
+/*
+** Minor collections will shift to major ones after LUAI_MINORMAJOR%
+** objects become old.
+*/
+#define LUAI_MINORMAJOR 100
+
+/*
+** Major collections will shift to minor ones after a collection
+** collects at least LUAI_MAJORMINOR% of the new objects.
+*/
+#define LUAI_MAJORMINOR 80
+
+/*
+** A young (minor) collection will run after creating LUAI_GENMINORMUL%
+** new objects.
+*/
+#define LUAI_GENMINORMUL 20
-#define LUAI_GENMAJORMUL 100 /* major multiplier */
-#define LUAI_GENMINORMUL 20 /* minor multiplier */
/* incremental */
@@ -187,27 +201,17 @@
/*
** Macros to set and apply GC parameters. GC parameters are given in
-** percentage points, but are stored as lu_byte. To reduce their
-** values and avoid repeated divisions by 100, these macros store
-** the original parameter multiplied by 2^n and divided by 100.
-** To apply them, the value is divided by 2^n (a shift) and then
-** multiplied by the stored parameter, yielding
-** value / 2^n * (original parameter * 2^n / 100), or approximately
-** (value * original parameter / 100).
-**
-** For most parameters, which are typically larger than 100%, 2^n is
-** 16 (2^4), allowing maximum values up to ~1500%, with a granularity
-** of ~6%. For the minor multiplier, which is typically smaller,
-** 2^n is 64 (2^6) to allow more precision. In that case, the maximum
-** value is ~400%, with a granularity of ~1.5%.
+** percentage points, but are stored as lu_byte. To avoid repeated
+** divisions by 100, these macros store the original parameter
+** multiplied by 128 and divided by 100. To apply them, if it first
+** divides the value by 128 it may lose precision; if it first
+** multiplies by the parameter, it may overflow. So, it first divides
+** by 32, then multiply by the parameter, and then divides the result by
+** 4.
*/
-#define gcparamshift(p) \
- (offsetof(global_State, p) == offsetof(global_State, genminormul) ? 6 : 4)
-
-#define setgcparam(g,p,v) \
- (g->p = (cast_uint(v) << gcparamshift(p)) / 100u)
-#define applygcparam(g,p,v) (((v) >> gcparamshift(p)) * g->p)
+#define setgcparam(g,p,v) (g->gcp##p = (cast_uint(v) << 7) / 100u)
+#define applygcparam(g,p,v) ((((v) >> 5) * g->gcp##p) >> 2)
/*
diff --git a/lstate.c b/lstate.c
@@ -368,8 +368,9 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud, unsigned int seed) {
setgcparam(g, gcpause, LUAI_GCPAUSE);
setgcparam(g, gcstepmul, LUAI_GCMUL);
g->gcstepsize = LUAI_GCSTEPSIZE;
- setgcparam(g, genmajormul, LUAI_GENMAJORMUL);
setgcparam(g, genminormul, LUAI_GENMINORMUL);
+ setgcparam(g, minormajor, LUAI_MINORMAJOR);
+ setgcparam(g, majorminor, LUAI_MAJORMINOR);
for (i=0; i < LUA_NUMTAGS; i++) g->mt[i] = NULL;
if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) {
/* memory allocation error: free partial state */
diff --git a/lstate.h b/lstate.h
@@ -264,16 +264,17 @@ typedef struct global_State {
TValue l_registry;
TValue nilvalue; /* a nil value */
unsigned int seed; /* randomized seed for hashes */
+ unsigned short gcpgenminormul; /* control minor generational collections */
+ unsigned short gcpmajorminor; /* control shift major->minor */
+ unsigned short gcpminormajor; /* control shift minor->major */
+ unsigned short gcpgcpause; /* size of pause between successive GCs */
+ unsigned short gcpgcstepmul; /* GC "speed" */
lu_byte currentwhite;
lu_byte gcstate; /* state of garbage collector */
lu_byte gckind; /* kind of GC running */
lu_byte gcstopem; /* stops emergency collections */
- lu_byte genminormul; /* control for minor generational collections */
- lu_byte genmajormul; /* control for major generational collections */
lu_byte gcstp; /* control whether GC is running */
lu_byte gcemergency; /* true if this is an emergency collection */
- lu_byte gcpause; /* size of pause between successive GCs */
- lu_byte gcstepmul; /* GC "speed" */
lu_byte gcstepsize; /* (log2 of) GC granularity */
GCObject *allgc; /* list of all collectable objects */
GCObject **sweepgc; /* current position of sweep in list */
diff --git a/lua.c b/lua.c
@@ -646,7 +646,7 @@ static int pmain (lua_State *L) {
luai_openlibs(L); /* open standard libraries */
createargtable(L, argv, argc, script); /* create table 'arg' */
lua_gc(L, LUA_GCRESTART); /* start GC... */
- lua_gc(L, LUA_GCGEN, 0, 0); /* ...in generational mode */
+ lua_gc(L, LUA_GCGEN, -1, -1, -1); /* ...in generational mode */
if (!(args & has_E)) { /* no option '-E'? */
if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */
return 0; /* error running LUA_INIT */
diff --git a/manual/manual.of b/manual/manual.of
@@ -621,7 +621,8 @@ that is inaccessible from Lua.
another live object refer to the object.)
Because Lua has no knowledge about @N{C code},
it never collects objects accessible through the registry @see{registry},
-which includes the global environment @see{globalenv}.
+which includes the global environment @see{globalenv} and
+the main thread.
The garbage collector (GC) in Lua can work in two modes:
@@ -638,8 +639,8 @@ therefore, optimal settings are also non-portable.
You can change the GC mode and parameters by calling
@Lid{lua_gc} @N{in C}
or @Lid{collectgarbage} in Lua.
-You can also use these functions to control
-the collector directly (e.g., to stop and restart it).
+You can also use these functions to control the collector directly,
+for instance to stop or restart it.
}
@@ -656,39 +657,36 @@ and the @def{garbage-collector step size}.
The garbage-collector pause
controls how long the collector waits before starting a new cycle.
-The collector starts a new cycle when the use of memory
-hits @M{n%} of the use after the previous collection.
+The collector starts a new cycle when the number of objects
+hits @M{n%} of the total after the previous collection.
Larger values make the collector less aggressive.
Values equal to or less than 100 mean the collector will not wait to
start a new cycle.
-A value of 200 means that the collector waits for the total memory in use
-to double before starting a new cycle.
+A value of 200 means that the collector waits for
+the total number of objects to double before starting a new cycle.
The default value is 200; the maximum value is 1000.
The garbage-collector step multiplier
controls the speed of the collector relative to
-memory allocation,
+object creation,
that is,
-how many elements it marks or sweeps for each
-kilobyte of memory allocated.
-Larger values make the collector more aggressive but also increase
-the size of each incremental step.
-You should not use values less than 100,
-because they make the collector too slow and
-can result in the collector never finishing a cycle.
-The default value is 100; the maximum value is 1000.
+how many objects it marks or sweeps for each object created.
+Larger values make the collector more aggressive.
+Beware that values too small can
+make the collector too slow to ever finish a cycle.
+The default value is 300; the maximum value is 1000.
The garbage-collector step size controls the
size of each incremental step,
-specifically how many bytes the interpreter allocates
+specifically how many objects the interpreter creates
before performing a step.
This parameter is logarithmic:
-A value of @M{n} means the interpreter will allocate @M{2@sp{n}}
-bytes between steps and perform equivalent work during the step.
+A value of @M{n} means the interpreter will create @M{2@sp{n}}
+objects between steps and perform equivalent work during the step.
A large value (e.g., 60) makes the collector a stop-the-world
(non-incremental) collector.
-The default value is 13,
-which means steps of approximately @N{8 Kbytes}.
+The default value is 8,
+which means steps of approximately @N{256 objects}.
}
@@ -697,31 +695,44 @@ which means steps of approximately @N{8 Kbytes}.
In generational mode,
the collector does frequent @emph{minor} collections,
which traverses only objects recently created.
-If after a minor collection the use of memory is still above a limit,
-the collector does a stop-the-world @emph{major} collection,
+If after a minor collection the number of objects is above a limit,
+the collector shifts to a @emph{major} collection,
which traverses all objects.
-The generational mode uses two parameters:
-the @def{minor multiplier} and the @def{the major multiplier}.
+The collector will then stay doing major collections until
+it detects that the program is generating enough garbage to justify
+going back to minor collections.
+
+The generational mode uses three parameters:
+the @def{minor multiplier}, the @def{minor-major multiplier},
+and the @def{major-minor multiplier}.
The minor multiplier controls the frequency of minor collections.
For a minor multiplier @M{x},
-a new minor collection will be done when memory
-grows @M{x%} larger than the memory in use after the previous major
-collection.
+a new minor collection will be done when the number of objects
+grows @M{x%} larger than the number in use just after the last collection.
For instance, for a multiplier of 20,
-the collector will do a minor collection when the use of memory
-gets 20% larger than the use after the previous major collection.
-The default value is 20; the maximum value is 200.
-
-The major multiplier controls the frequency of major collections.
-For a major multiplier @M{x},
-a new major collection will be done when memory
-grows @M{x%} larger than the memory in use after the previous major
-collection.
+the collector will do a minor collection when the number of objects
+gets 20% larger than the total after the last major collection.
+The default value is 20.
+
+The minor-major multiplier controls the shift to major collections.
+For a multiplier @M{x},
+the collector will shift to a major collection
+when the number of old objects grows @M{x%} larger
+than the total after the previous major collection.
For instance, for a multiplier of 100,
-the collector will do a major collection when the use of memory
-gets larger than twice the use after the previous collection.
-The default value is 100; the maximum value is 1000.
+the collector will do a major collection when the number of old objects
+gets larger than twice the total after the previous major collection.
+The default value is 100.
+
+The major-minor multiplier controls the shift back to minor collections.
+For a multiplier @M{x},
+the collector will shift back to minor collections
+after a major collection collects at least @M{x%} of the allocated objects.
+In particular, for a multiplier of 0,
+the collector will immediately shift back to minor collections
+after doing one cycle of major collections.
+The default value is 20.
}
@@ -3311,9 +3322,8 @@ Returns the remainder of dividing the current amount of bytes of
memory in use by Lua by 1024.
}
-@item{@id{LUA_GCSTEP} @T{(int stepsize)}|
-Performs an incremental step of garbage collection,
-corresponding to the allocation of @id{stepsize} Kbytes.
+@item{@id{LUA_GCSTEP}|
+Performs a step of garbage collection.
}
@item{@id{LUA_GCISRUNNING}|
@@ -3321,13 +3331,13 @@ Returns a boolean that tells whether the collector is running
(i.e., not stopped).
}
-@item{@id{LUA_GCINC} (int pause, int stepmul, stepsize)|
+@item{@id{LUA_GCINC} (int pause, int stepmul, int stepsize)|
Changes the collector to incremental mode
with the given parameters @see{incmode}.
Returns the previous mode (@id{LUA_GCGEN} or @id{LUA_GCINC}).
}
-@item{@id{LUA_GCGEN} (int minormul, int majormul)|
+@item{@id{LUA_GCGEN} (int minormul, int minormajor, int majorminor)|
Changes the collector to generational mode
with the given parameters @see{genmode}.
Returns the previous mode (@id{LUA_GCGEN} or @id{LUA_GCINC}).
@@ -6312,13 +6322,14 @@ gives the exact number of bytes in use by Lua.
@item{@St{step}|
Performs a garbage-collection step.
-The step @Q{size} is controlled by @id{arg}.
-With a zero value,
-the collector will perform one basic (indivisible) step.
-For non-zero values,
-the collector will perform as if that amount of memory
-(in Kbytes) had been allocated by Lua.
-Returns @true if the step finished a collection cycle.
+In incremental mode,
+that step corresponds to the current step size;
+the function returns @true if the step finished a collection cycle.
+In generational mode,
+the step performs a full minor collection or
+a major collection,
+if the collector has scheduled one;
+the function returns @true if the step performed a major collection.
}
@item{@St{isrunning}|
@@ -6332,15 +6343,15 @@ This option can be followed by three numbers:
the garbage-collector pause,
the step multiplier,
and the step size @see{incmode}.
-A zero means to not change that value.
+A -1 or absent value means to not change that value.
}
@item{@St{generational}|
Change the collector mode to generational.
-This option can be followed by two numbers:
-the garbage-collector minor multiplier
-and the major multiplier @see{genmode}.
-A zero means to not change that value.
+This option can be followed by three numbers:
+the garbage-collector minor multiplier,
+the minor-major multiplier, and the major-minor multiplier @see{genmode}.
+A -1 or absent value means to not change that value.
}
}
@@ -9229,6 +9240,9 @@ declare a local variable with the same name in the loop body.
@itemize{
@item{
+There were several changes in the parameters
+for the options @St{incremental} and @St{generational}
+of the function @Lid{collectgarbage}.
}
}
@@ -9245,6 +9259,12 @@ it is equivalent to @Lid{lua_closethread} with
@id{from} being @id{NULL}.
}
+@item{
+There were several changes in the parameters
+for the options @Lid{LUA_GCINC} and @Lid{LUA_GCGEN}
+of the function @Lid{lua_gc}.
+}
+
}
}