diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2022-11-29 10:37:08 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2022-11-29 10:37:08 -0300 |
commit | d324a0ccf9e2511baf182dd981a8ee9835cee925 (patch) | |
tree | 7cb36618c0cbafb4a0216a117fd25a78250991a5 /lgc.c | |
parent | 152b51955aabb9dfb32302569fac810e999eda03 (diff) | |
download | lua-d324a0ccf9e2511baf182dd981a8ee9835cee925.tar.gz lua-d324a0ccf9e2511baf182dd981a8ee9835cee925.tar.bz2 lua-d324a0ccf9e2511baf182dd981a8ee9835cee925.zip |
Simpler control for major collections
Diffstat (limited to 'lgc.c')
-rw-r--r-- | lgc.c | 190 |
1 files changed, 70 insertions, 120 deletions
@@ -29,23 +29,18 @@ | |||
29 | 29 | ||
30 | 30 | ||
31 | /* | 31 | /* |
32 | ** Maximum number of elements to sweep in each single step. | 32 | ** Number of fixed (luaC_fix) objects in a Lua state: metafield names, |
33 | ** (Large enough to dissipate fixed overheads but small enough | 33 | ** plus reserved words, plus "_ENV", plus the memory-error message. |
34 | ** to allow small steps for the collector.) | ||
35 | */ | 34 | */ |
36 | #define GCSWEEPMAX 20 | 35 | #define NFIXED (TM_N + NUM_RESERVED + 2) |
37 | |||
38 | /* | ||
39 | ** Maximum number of finalizers to call in each single step. | ||
40 | */ | ||
41 | #define GCFINMAX 10 | ||
42 | 36 | ||
43 | 37 | ||
44 | /* | 38 | /* |
45 | ** Cost of calling one finalizer. | 39 | ** Maximum number of elements to sweep in each single step. |
40 | ** (Large enough to dissipate fixed overheads but small enough | ||
41 | ** to allow small steps for the collector.) | ||
46 | */ | 42 | */ |
47 | #define GCFINALIZECOST 50 | 43 | #define GCSWEEPMAX 20 |
48 | |||
49 | 44 | ||
50 | 45 | ||
51 | /* mask with all color bits */ | 46 | /* mask with all color bits */ |
@@ -205,7 +200,7 @@ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { | |||
205 | } | 200 | } |
206 | else { /* sweep phase */ | 201 | else { /* sweep phase */ |
207 | lua_assert(issweepphase(g)); | 202 | lua_assert(issweepphase(g)); |
208 | if (g->gckind == KGC_INC) /* incremental mode? */ | 203 | if (g->gckind != KGC_GEN) /* incremental mode? */ |
209 | makewhite(g, o); /* mark 'o' as white to avoid other barriers */ | 204 | makewhite(g, o); /* mark 'o' as white to avoid other barriers */ |
210 | } | 205 | } |
211 | } | 206 | } |
@@ -398,7 +393,7 @@ static void cleargraylists (global_State *g) { | |||
398 | */ | 393 | */ |
399 | static void restartcollection (global_State *g) { | 394 | static void restartcollection (global_State *g) { |
400 | cleargraylists(g); | 395 | cleargraylists(g); |
401 | g->marked = TM_N + NUM_RESERVED + 2; | 396 | g->marked = NFIXED; |
402 | markobject(g, g->mainthread); | 397 | markobject(g, g->mainthread); |
403 | markvalue(g, &g->l_registry); | 398 | markvalue(g, &g->l_registry); |
404 | markmt(g); | 399 | markmt(g); |
@@ -927,17 +922,6 @@ static void GCTM (lua_State *L) { | |||
927 | 922 | ||
928 | 923 | ||
929 | /* | 924 | /* |
930 | ** Call a few finalizers | ||
931 | */ | ||
932 | static void runafewfinalizers (lua_State *L, int n) { | ||
933 | global_State *g = G(L); | ||
934 | int i; | ||
935 | for (i = 0; i < n && g->tobefnz; i++) | ||
936 | GCTM(L); /* call one finalizer */ | ||
937 | } | ||
938 | |||
939 | |||
940 | /* | ||
941 | ** call all pending finalizers | 925 | ** call all pending finalizers |
942 | */ | 926 | */ |
943 | static void callallpendingfinalizers (lua_State *L) { | 927 | static void callallpendingfinalizers (lua_State *L) { |
@@ -1295,8 +1279,7 @@ static void atomic2gen (lua_State *L, global_State *g) { | |||
1295 | sweep2old(L, &g->tobefnz); | 1279 | sweep2old(L, &g->tobefnz); |
1296 | 1280 | ||
1297 | g->gckind = KGC_GEN; | 1281 | g->gckind = KGC_GEN; |
1298 | g->lastatomic = 0; | 1282 | g->GClastmajor = gettotalobjs(g); /* base for memory control */ |
1299 | g->GCestimate = gettotalobjs(g); /* base for memory control */ | ||
1300 | finishgencycle(L, g); | 1283 | finishgencycle(L, g); |
1301 | } | 1284 | } |
1302 | 1285 | ||
@@ -1306,7 +1289,7 @@ static void atomic2gen (lua_State *L, global_State *g) { | |||
1306 | ** total number of objects grows 'genminormul'%. | 1289 | ** total number of objects grows 'genminormul'%. |
1307 | */ | 1290 | */ |
1308 | static void setminordebt (global_State *g) { | 1291 | static void setminordebt (global_State *g) { |
1309 | luaE_setdebt(g, -(cast(l_obj, (gettotalobjs(g) / 100)) * g->genminormul)); | 1292 | luaE_setdebt(g, -(gettotalobjs(g) / 100) * g->genminormul); |
1310 | } | 1293 | } |
1311 | 1294 | ||
1312 | 1295 | ||
@@ -1338,7 +1321,6 @@ static void enterinc (global_State *g) { | |||
1338 | g->finobjrold = g->finobjold1 = g->finobjsur = NULL; | 1321 | g->finobjrold = g->finobjold1 = g->finobjsur = NULL; |
1339 | g->gcstate = GCSpause; | 1322 | g->gcstate = GCSpause; |
1340 | g->gckind = KGC_INC; | 1323 | g->gckind = KGC_INC; |
1341 | g->lastatomic = 0; | ||
1342 | } | 1324 | } |
1343 | 1325 | ||
1344 | 1326 | ||
@@ -1347,13 +1329,18 @@ static void enterinc (global_State *g) { | |||
1347 | */ | 1329 | */ |
1348 | void luaC_changemode (lua_State *L, int newmode) { | 1330 | void luaC_changemode (lua_State *L, int newmode) { |
1349 | global_State *g = G(L); | 1331 | global_State *g = G(L); |
1350 | if (newmode != g->gckind) { | 1332 | if (newmode != g->gckind) { /* does it need to change? */ |
1351 | if (newmode == KGC_GEN) /* entering generational mode? */ | 1333 | if (newmode == KGC_INC) { /* entering incremental mode? */ |
1334 | if (g->gckind == KGC_GENMAJOR) | ||
1335 | g->gckind = KGC_INC; /* already incremental but in name */ | ||
1336 | else | ||
1337 | enterinc(g); /* entering incremental mode */ | ||
1338 | } | ||
1339 | else { | ||
1340 | lua_assert(newmode == KGC_GEN); | ||
1352 | entergen(L, g); | 1341 | entergen(L, g); |
1353 | else | 1342 | } |
1354 | enterinc(g); /* entering incremental mode */ | ||
1355 | } | 1343 | } |
1356 | g->lastatomic = 0; | ||
1357 | } | 1344 | } |
1358 | 1345 | ||
1359 | 1346 | ||
@@ -1367,93 +1354,52 @@ static void fullgen (lua_State *L, global_State *g) { | |||
1367 | 1354 | ||
1368 | 1355 | ||
1369 | /* | 1356 | /* |
1370 | ** Does a major collection after last collection was a "bad collection". | 1357 | ** Does a major collector up to the atomic phase and then either |
1371 | ** | 1358 | ** returns to minor collections or stays doing major ones. If the |
1372 | ** When the program is building a big structure, it allocates lots of | 1359 | ** number of objects collected this time (numobjs - marked) is more than |
1373 | ** memory but generates very little garbage. In those scenarios, | 1360 | ** half the number of objects created since the last major collection |
1374 | ** the generational mode just wastes time doing small collections, and | 1361 | ** (numobjs - lastmajor), it goes back to minor collections. |
1375 | ** major collections are frequently what we call a "bad collection", a | 1362 | */ |
1376 | ** collection that frees too few objects. To avoid the cost of switching | 1363 | static void genmajorstep (lua_State *L, global_State *g) { |
1377 | ** between generational mode and the incremental mode needed for full | 1364 | l_obj lastmajor = g->GClastmajor; /* count from last collection */ |
1378 | ** (major) collections, the collector tries to stay in incremental mode | 1365 | l_obj numobjs = gettotalobjs(g); /* current count */ |
1379 | ** after a bad collection, and to switch back to generational mode only | ||
1380 | ** after a "good" collection (one that traverses less than 9/8 objects | ||
1381 | ** of the previous one). | ||
1382 | ** The collector must choose whether to stay in incremental mode or to | ||
1383 | ** switch back to generational mode before sweeping. At this point, it | ||
1384 | ** does not know the real memory in use, so it cannot use memory to | ||
1385 | ** decide whether to return to generational mode. Instead, it uses the | ||
1386 | ** number of objects traversed (returned by 'atomic') as a proxy. The | ||
1387 | ** field 'g->lastatomic' keeps this count from the last collection. | ||
1388 | ** ('g->lastatomic != 0' also means that the last collection was bad.) | ||
1389 | */ | ||
1390 | static void stepgenfull (lua_State *L, global_State *g) { | ||
1391 | lu_mem lastatomic = g->lastatomic; /* count from last collection */ | ||
1392 | if (g->gckind == KGC_GEN) /* still in generational mode? */ | ||
1393 | enterinc(g); /* enter incremental mode */ | ||
1394 | luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ | 1366 | luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ |
1395 | g->marked = 0; | ||
1396 | atomic(L); /* mark everybody */ | 1367 | atomic(L); /* mark everybody */ |
1397 | if (g->marked < lastatomic + (lastatomic >> 3)) { /* good collection? */ | 1368 | if ((numobjs - g->marked) > ((numobjs - lastmajor) >> 1)) { |
1398 | atomic2gen(L, g); /* return to generational mode */ | 1369 | atomic2gen(L, g); /* return to generational mode */ |
1399 | setminordebt(g); | 1370 | setminordebt(g); |
1400 | } | 1371 | } |
1401 | else { /* another bad collection; stay in incremental mode */ | 1372 | else { /* bad collection; stay in major mode */ |
1402 | g->GCestimate = gettotalobjs(g); /* first estimate */; | ||
1403 | g->lastatomic = g->marked; | ||
1404 | entersweep(L); | 1373 | entersweep(L); |
1405 | luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ | 1374 | luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ |
1406 | setpause(g); | 1375 | setpause(g); |
1376 | g->GClastmajor = gettotalobjs(g); | ||
1407 | } | 1377 | } |
1408 | } | 1378 | } |
1409 | 1379 | ||
1410 | 1380 | ||
1411 | /* | 1381 | /* |
1412 | ** Does a generational "step". | 1382 | ** Does a generational "step". If the total number of objects grew |
1413 | ** Usually, this means doing a minor collection and setting the debt to | 1383 | ** more than 'majormul'% since the last major collection, does a |
1414 | ** make another collection when memory grows 'genminormul'% larger. | 1384 | ** major collection. Otherwise, does a minor collection. The test |
1415 | ** | 1385 | ** ('GCdebt' > 0) avoids major collections when the step originated from |
1416 | ** However, there are exceptions. If memory grows 'genmajormul'% | 1386 | ** 'collectgarbage("step")'. |
1417 | ** larger than it was at the end of the last major collection (kept | ||
1418 | ** in 'g->GCestimate'), the function does a major collection. At the | ||
1419 | ** end, it checks whether the major collection was able to free a | ||
1420 | ** decent amount of memory (at least half the growth in memory since | ||
1421 | ** previous major collection). If so, the collector keeps its state, | ||
1422 | ** and the next collection will probably be minor again. Otherwise, | ||
1423 | ** we have what we call a "bad collection". In that case, set the field | ||
1424 | ** 'g->lastatomic' to signal that fact, so that the next collection will | ||
1425 | ** go to 'stepgenfull'. | ||
1426 | ** | ||
1427 | ** 'GCdebt <= 0' means an explicit call to GC step with "size" zero; | ||
1428 | ** in that case, do a minor collection. | ||
1429 | */ | 1387 | */ |
1430 | static void genstep (lua_State *L, global_State *g) { | 1388 | static void genstep (lua_State *L, global_State *g) { |
1431 | if (g->lastatomic != 0) /* last collection was a bad one? */ | 1389 | l_obj majorbase = g->GClastmajor; /* count after last major collection */ |
1432 | stepgenfull(L, g); /* do a full step */ | 1390 | l_obj majorinc = (majorbase / 100) * getgcparam(g->genmajormul); |
1433 | else { | 1391 | if (g->GCdebt > 0 && gettotalobjs(g) > majorbase + majorinc) { |
1434 | l_obj majorbase = g->GCestimate; /* objects after last major collection */ | 1392 | /* do a major collection */ |
1435 | l_obj majorinc = (majorbase / 100) * getgcparam(g->genmajormul); | 1393 | enterinc(g); |
1436 | if (g->GCdebt > 0 && gettotalobjs(g) > majorbase + majorinc) { | 1394 | g->gckind = KGC_GENMAJOR; |
1437 | g->marked = 0; | 1395 | genmajorstep(L, g); |
1438 | fullgen(L, g); /* do a major collection */ | 1396 | } |
1439 | if (gettotalobjs(g) < majorbase + (majorinc / 2)) { | 1397 | else { /* regular case; do a minor collection */ |
1440 | /* collected at least half of object growth since last major | 1398 | g->marked = 0; |
1441 | collection; keep doing minor collections. */ | 1399 | youngcollection(L, g); |
1442 | lua_assert(g->lastatomic == 0); | 1400 | setminordebt(g); |
1443 | } | 1401 | lua_assert(g->GClastmajor == majorbase); |
1444 | else { /* bad collection */ | ||
1445 | g->lastatomic = g->marked; /* signal that last collection was bad */ | ||
1446 | setpause(g); /* do a long wait for next (major) collection */ | ||
1447 | } | ||
1448 | } | ||
1449 | else { /* regular case; do a minor collection */ | ||
1450 | g->marked = 0; | ||
1451 | youngcollection(L, g); | ||
1452 | setminordebt(g); | ||
1453 | g->GCestimate = majorbase; /* preserve base value */ | ||
1454 | } | ||
1455 | } | 1402 | } |
1456 | lua_assert(isdecGCmodegen(g)); | ||
1457 | } | 1403 | } |
1458 | 1404 | ||
1459 | /* }====================================================== */ | 1405 | /* }====================================================== */ |
@@ -1557,11 +1503,8 @@ static l_obj atomic (lua_State *L) { | |||
1557 | 1503 | ||
1558 | static void sweepstep (lua_State *L, global_State *g, | 1504 | static void sweepstep (lua_State *L, global_State *g, |
1559 | int nextstate, GCObject **nextlist) { | 1505 | int nextstate, GCObject **nextlist) { |
1560 | if (g->sweepgc) { | 1506 | if (g->sweepgc) |
1561 | l_obj olddebt = g->GCdebt; | ||
1562 | g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); | 1507 | g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); |
1563 | g->GCestimate += g->GCdebt - olddebt; /* update estimate */ | ||
1564 | } | ||
1565 | else { /* enter next state */ | 1508 | else { /* enter next state */ |
1566 | g->gcstate = nextstate; | 1509 | g->gcstate = nextstate; |
1567 | g->sweepgc = nextlist; | 1510 | g->sweepgc = nextlist; |
@@ -1618,11 +1561,11 @@ static l_obj singlestep (lua_State *L) { | |||
1618 | work = 0; | 1561 | work = 0; |
1619 | break; | 1562 | break; |
1620 | } | 1563 | } |
1621 | case GCScallfin: { /* call remaining finalizers */ | 1564 | case GCScallfin: { /* call finalizers */ |
1622 | if (g->tobefnz && !g->gcemergency) { | 1565 | if (g->tobefnz && !g->gcemergency) { |
1623 | g->gcstopem = 0; /* ok collections during finalizers */ | 1566 | g->gcstopem = 0; /* ok collections during finalizers */ |
1624 | runafewfinalizers(L, GCFINMAX); | 1567 | GCTM(L); /* call one finalizer */ |
1625 | work = GCFINMAX * GCFINALIZECOST; | 1568 | work = 1; |
1626 | } | 1569 | } |
1627 | else { /* emergency mode or no more finalizers */ | 1570 | else { /* emergency mode or no more finalizers */ |
1628 | g->gcstate = GCSpause; /* finish collection */ | 1571 | g->gcstate = GCSpause; /* finish collection */ |
@@ -1679,10 +1622,17 @@ void luaC_step (lua_State *L) { | |||
1679 | global_State *g = G(L); | 1622 | global_State *g = G(L); |
1680 | lua_assert(!g->gcemergency); | 1623 | lua_assert(!g->gcemergency); |
1681 | if (gcrunning(g)) { /* running? */ | 1624 | if (gcrunning(g)) { /* running? */ |
1682 | if(isdecGCmodegen(g)) | 1625 | switch (g->gckind) { |
1683 | genstep(L, g); | 1626 | case KGC_INC: |
1684 | else | 1627 | incstep(L, g); |
1685 | incstep(L, g); | 1628 | break; |
1629 | case KGC_GEN: | ||
1630 | genstep(L, g); | ||
1631 | break; | ||
1632 | case KGC_GENMAJOR: | ||
1633 | genmajorstep(L, g); | ||
1634 | break; | ||
1635 | } | ||
1686 | } | 1636 | } |
1687 | } | 1637 | } |
1688 | 1638 | ||
@@ -1700,7 +1650,7 @@ static void fullinc (lua_State *L, global_State *g) { | |||
1700 | /* finish any pending sweep phase to start a new cycle */ | 1650 | /* finish any pending sweep phase to start a new cycle */ |
1701 | luaC_runtilstate(L, bitmask(GCSpause)); | 1651 | luaC_runtilstate(L, bitmask(GCSpause)); |
1702 | luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */ | 1652 | luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */ |
1703 | /* estimate must be correct after a full GC cycle */ | 1653 | /* 'marked' must be correct after a full GC cycle */ |
1704 | lua_assert(g->marked == gettotalobjs(g)); | 1654 | lua_assert(g->marked == gettotalobjs(g)); |
1705 | luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ | 1655 | luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ |
1706 | setpause(g); | 1656 | setpause(g); |
@@ -1716,10 +1666,10 @@ void luaC_fullgc (lua_State *L, int isemergency) { | |||
1716 | global_State *g = G(L); | 1666 | global_State *g = G(L); |
1717 | lua_assert(!g->gcemergency); | 1667 | lua_assert(!g->gcemergency); |
1718 | g->gcemergency = isemergency; /* set flag */ | 1668 | g->gcemergency = isemergency; /* set flag */ |
1719 | if (g->gckind == KGC_INC) | 1669 | if (g->gckind == KGC_GEN) |
1720 | fullinc(L, g); | ||
1721 | else | ||
1722 | fullgen(L, g); | 1670 | fullgen(L, g); |
1671 | else | ||
1672 | fullinc(L, g); | ||
1723 | g->gcemergency = 0; | 1673 | g->gcemergency = 0; |
1724 | } | 1674 | } |
1725 | 1675 | ||