diff options
Diffstat (limited to 'lgc.c')
-rw-r--r-- | lgc.c | 158 |
1 files changed, 114 insertions, 44 deletions
@@ -101,6 +101,7 @@ | |||
101 | 101 | ||
102 | static void reallymarkobject (global_State *g, GCObject *o); | 102 | static void reallymarkobject (global_State *g, GCObject *o); |
103 | static lu_mem atomic (lua_State *L); | 103 | static lu_mem atomic (lua_State *L); |
104 | static void entersweep (lua_State *L); | ||
104 | 105 | ||
105 | 106 | ||
106 | /* | 107 | /* |
@@ -1162,15 +1163,7 @@ static void youngcollection (lua_State *L, global_State *g) { | |||
1162 | } | 1163 | } |
1163 | 1164 | ||
1164 | 1165 | ||
1165 | /* | 1166 | static void atomic2gen (lua_State *L, global_State *g) { |
1166 | ** Enter generational mode. Must go until the end of an atomic cycle | ||
1167 | ** to ensure that all threads are in the gray list. Then, turn all | ||
1168 | ** objects into old and finishes the collection. | ||
1169 | */ | ||
1170 | static void entergen (lua_State *L, global_State *g) { | ||
1171 | luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */ | ||
1172 | luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ | ||
1173 | atomic(L); | ||
1174 | /* sweep all elements making them old */ | 1167 | /* sweep all elements making them old */ |
1175 | sweep2old(L, &g->allgc); | 1168 | sweep2old(L, &g->allgc); |
1176 | /* everything alive now is old */ | 1169 | /* everything alive now is old */ |
@@ -1183,15 +1176,31 @@ static void entergen (lua_State *L, global_State *g) { | |||
1183 | sweep2old(L, &g->tobefnz); | 1176 | sweep2old(L, &g->tobefnz); |
1184 | 1177 | ||
1185 | g->gckind = KGC_GEN; | 1178 | g->gckind = KGC_GEN; |
1179 | g->lastatomic = 0; | ||
1186 | g->GCestimate = gettotalbytes(g); /* base for memory control */ | 1180 | g->GCestimate = gettotalbytes(g); /* base for memory control */ |
1187 | finishgencycle(L, g); | 1181 | finishgencycle(L, g); |
1188 | } | 1182 | } |
1189 | 1183 | ||
1190 | 1184 | ||
1191 | /* | 1185 | /* |
1186 | ** Enter generational mode. Must go until the end of an atomic cycle | ||
1187 | ** to ensure that all threads and weak tables are in the gray lists. | ||
1188 | ** Then, turn all objects into old and finishes the collection. | ||
1189 | */ | ||
1190 | static lu_mem entergen (lua_State *L, global_State *g) { | ||
1191 | lu_mem numobjs; | ||
1192 | luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */ | ||
1193 | luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ | ||
1194 | numobjs = atomic(L); /* propagates all and then do the atomic stuff */ | ||
1195 | atomic2gen(L, g); | ||
1196 | return numobjs; | ||
1197 | } | ||
1198 | |||
1199 | |||
1200 | /* | ||
1192 | ** Enter incremental mode. Turn all objects white, make all | 1201 | ** Enter incremental mode. Turn all objects white, make all |
1193 | ** intermediate lists point to NULL (to avoid invalid pointers), | 1202 | ** intermediate lists point to NULL (to avoid invalid pointers), |
1194 | ** and go to pause state. | 1203 | ** and go to the pause state. |
1195 | */ | 1204 | */ |
1196 | static void enterinc (global_State *g) { | 1205 | static void enterinc (global_State *g) { |
1197 | whitelist(g, g->allgc); | 1206 | whitelist(g, g->allgc); |
@@ -1201,6 +1210,7 @@ static void enterinc (global_State *g) { | |||
1201 | g->finobjrold = g->finobjold = g->finobjsur = NULL; | 1210 | g->finobjrold = g->finobjold = g->finobjsur = NULL; |
1202 | g->gcstate = GCSpause; | 1211 | g->gcstate = GCSpause; |
1203 | g->gckind = KGC_INC; | 1212 | g->gckind = KGC_INC; |
1213 | g->lastatomic = 0; | ||
1204 | } | 1214 | } |
1205 | 1215 | ||
1206 | 1216 | ||
@@ -1215,54 +1225,114 @@ void luaC_changemode (lua_State *L, int newmode) { | |||
1215 | else | 1225 | else |
1216 | enterinc(g); /* entering incremental mode */ | 1226 | enterinc(g); /* entering incremental mode */ |
1217 | } | 1227 | } |
1228 | g->lastatomic = 0; | ||
1218 | } | 1229 | } |
1219 | 1230 | ||
1220 | 1231 | ||
1221 | /* | 1232 | /* |
1222 | ** Does a full collection in generational mode. | 1233 | ** Does a full collection in generational mode. |
1223 | */ | 1234 | */ |
1224 | static void fullgen (lua_State *L, global_State *g) { | 1235 | static lu_mem fullgen (lua_State *L, global_State *g) { |
1225 | enterinc(g); | 1236 | enterinc(g); |
1226 | entergen(L, g); | 1237 | return entergen(L, g); |
1238 | } | ||
1239 | |||
1240 | |||
1241 | /* | ||
1242 | ** Set debt for the next minor collection, which will happen when | ||
1243 | ** memory grows 'genminormul'%. | ||
1244 | */ | ||
1245 | static void setminordebt (global_State *g) { | ||
1246 | luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul)); | ||
1227 | } | 1247 | } |
1228 | 1248 | ||
1229 | 1249 | ||
1230 | /* | 1250 | /* |
1231 | ** Does a generational "step". If memory grows 'genmajormul'% larger | 1251 | ** Does a major collection after last collection was a "bad collection". |
1232 | ** than last major collection (kept in 'g->GCestimate'), does a major | 1252 | ** |
1233 | ** collection. Otherwise, does a minor collection and set debt to make | 1253 | ** When the program is building a big struture, it allocates lots of |
1234 | ** another collection when memory grows 'genminormul'% larger. | 1254 | ** memory but generates very little garbage. In those scenarios, |
1235 | ** When it does a major collection, it then checks whether it could | 1255 | ** the generational mode just wastes time doing small collections, and |
1236 | ** reclaim at least ?? memory. If not, it sets a long pause for the | 1256 | ** major collections are frequently what we call a "bad collection", a |
1237 | ** next collection. (Therefore, the next collection will be a major | 1257 | ** collection that frees too few objects. To avoid the cost of switching |
1238 | ** one, too.) | 1258 | ** between generational mode and the incremental mode needed for full |
1259 | ** (major) collections, the collector tries to stay in incremental mode | ||
1260 | ** after a bad collection, and to switch back to generational mode only | ||
1261 | ** after a "good" collection (one that traverses less than 9/8 objects | ||
1262 | ** of the previous one). | ||
1263 | ** The collector must choose whether to stay in incremental mode or to | ||
1264 | ** switch back to generational mode before sweeping. At this point, it | ||
1265 | ** does not know the real memory in use, so it cannot use memory to | ||
1266 | ** decide whether to return to generational mode. Instead, it uses the | ||
1267 | ** number of objects traversed (returned by 'atomic') as a proxy. The | ||
1268 | ** field 'g->lastatomic' keeps this count from the last collection. | ||
1269 | ** ('g->lastatomic != 0' also means that the last collection was bad.) | ||
1270 | */ | ||
1271 | static void stepgenfull (lua_State *L, global_State *g) { | ||
1272 | lu_mem newatomic; /* count of traversed objects */ | ||
1273 | lu_mem lastatomic = g->lastatomic; /* count from last collection */ | ||
1274 | if (g->gckind == KGC_GEN) /* still in generational mode? */ | ||
1275 | enterinc(g); /* enter incremental mode */ | ||
1276 | luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ | ||
1277 | newatomic = atomic(L); /* mark everybody */ | ||
1278 | if (newatomic < lastatomic + (lastatomic >> 3)) { /* good collection? */ | ||
1279 | atomic2gen(L, g); /* return to generational mode */ | ||
1280 | setminordebt(g); | ||
1281 | } | ||
1282 | else { /* another bad collection; stay in incremental mode */ | ||
1283 | g->GCestimate = gettotalbytes(g); /* first estimate */; | ||
1284 | entersweep(L); | ||
1285 | luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ | ||
1286 | setpause(g); | ||
1287 | g->lastatomic = newatomic; | ||
1288 | } | ||
1289 | } | ||
1290 | |||
1291 | |||
1292 | /* | ||
1293 | ** Does a generational "step". | ||
1294 | ** Usually, this means doing a minor collection and setting the debt to | ||
1295 | ** make another collection when memory grows 'genminormul'% larger. | ||
1296 | ** | ||
1297 | ** However, there are exceptions. If memory grows 'genmajormul'% | ||
1298 | ** larger than it was at the end of the last major collection (kept | ||
1299 | ** in 'g->GCestimate'), the function does a major collection. At the | ||
1300 | ** end, it checks whether the major collection was able to free a | ||
1301 | ** decent amount of memory (at least half the growth in memory since | ||
1302 | ** previous major collection). If so, the collector keeps its state, | ||
1303 | ** and the next collection will probably be minor again. Otherwise, | ||
1304 | ** we have what we call a "bad collection". In that case, set the field | ||
1305 | ** 'g->lastatomic' to signal that fact, so that the next collection will | ||
1306 | ** go to 'stepgenfull'. | ||
1307 | ** | ||
1239 | ** 'GCdebt <= 0' means an explicit call to GC step with "size" zero; | 1308 | ** 'GCdebt <= 0' means an explicit call to GC step with "size" zero; |
1240 | ** in that case, always do a minor collection. | 1309 | ** in that case, do a minor collection. |
1241 | */ | 1310 | */ |
1242 | static void genstep (lua_State *L, global_State *g) { | 1311 | static void genstep (lua_State *L, global_State *g) { |
1243 | lu_mem majorbase = g->GCestimate; /* memory after last major collection */ | 1312 | if (g->lastatomic != 0) /* last collection was a bad one? */ |
1244 | lu_mem majorinc = (majorbase / 100) * getgcparam(g->genmajormul); | 1313 | stepgenfull(L, g); /* do a full step */ |
1245 | lu_mem memnew = gettotalbytes(g); | 1314 | else { |
1246 | if (g->GCdebt > 0 && memnew > majorbase + majorinc) { | 1315 | lu_mem majorbase = g->GCestimate; /* memory after last major collection */ |
1247 | fullgen(L, g); | 1316 | lu_mem majorinc = (majorbase / 100) * getgcparam(g->genmajormul); |
1248 | memnew = gettotalbytes(g); | 1317 | if (g->GCdebt > 0 && gettotalbytes(g) > majorbase + majorinc) { |
1249 | if (memnew < majorbase + (majorinc / 2)) { | 1318 | lu_mem numobjs = fullgen(L, g); /* do a major collection */ |
1250 | /* collected at least half of memory growth since last major | 1319 | if (gettotalbytes(g) < majorbase + (majorinc / 2)) { |
1251 | collection; go back to minor collections */ | 1320 | /* collected at least half of memory growth since last major |
1252 | luaE_setdebt(g, -(cast(l_mem, (memnew / 100)) * g->genminormul)); | 1321 | collection; keep doing minor collections */ |
1322 | setminordebt(g); | ||
1323 | } | ||
1324 | else { /* bad collection */ | ||
1325 | g->lastatomic = numobjs; /* signal that last collection was bad */ | ||
1326 | setpause(g); /* do a long wait for next (major) collection */ | ||
1327 | } | ||
1253 | } | 1328 | } |
1254 | else { | 1329 | else { /* regular case; do a minor collection */ |
1255 | /* memory seems to be growing; do a long wait for next (major) | 1330 | youngcollection(L, g); |
1256 | collection */ | 1331 | setminordebt(g); |
1257 | setpause(g); | 1332 | g->GCestimate = majorbase; /* preserve base value */ |
1258 | } | 1333 | } |
1259 | } | 1334 | } |
1260 | else { | 1335 | lua_assert(isdecGCmodegen(g)); |
1261 | youngcollection(L, g); | ||
1262 | memnew = gettotalbytes(g); | ||
1263 | luaE_setdebt(g, -(cast(l_mem, (memnew / 100)) * g->genminormul)); | ||
1264 | g->GCestimate = majorbase; /* preserve base value */ | ||
1265 | } | ||
1266 | } | 1336 | } |
1267 | 1337 | ||
1268 | /* }====================================================== */ | 1338 | /* }====================================================== */ |
@@ -1493,10 +1563,10 @@ static void incstep (lua_State *L, global_State *g) { | |||
1493 | void luaC_step (lua_State *L) { | 1563 | void luaC_step (lua_State *L) { |
1494 | global_State *g = G(L); | 1564 | global_State *g = G(L); |
1495 | if (g->gcrunning) { /* running? */ | 1565 | if (g->gcrunning) { /* running? */ |
1496 | if (g->gckind == KGC_INC) | 1566 | if(isdecGCmodegen(g)) |
1497 | incstep(L, g); | ||
1498 | else | ||
1499 | genstep(L, g); | 1567 | genstep(L, g); |
1568 | else | ||
1569 | incstep(L, g); | ||
1500 | } | 1570 | } |
1501 | } | 1571 | } |
1502 | 1572 | ||