lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


> Do __gc methods actually work for tables these days?
> Last time I tried I
> couldn't get it to... seemed like a fairly serious problem.

No, they don't. Only userdata.

> It seems that the first garbage collection pass will
> free a table ( the class members are in a table ), but will not free
> newly unreferenced members of that table.

There is an important issue about finalisation (that is, __gc metamethods).
This is not restricted to Lua, by the way -- every garbage collector which
implements finalisation methods must find some way of dealing with this
issue, and most choose the same one.)

Basically, a finalisable object requires (at least) two garbage collection
cycles to actually disappear. The first GC which identifies the object as
unreachable only causes the finalisation method to run (and marks the
object as having been finalised). The second GC which identifies the object
as unreachable *and finalised* then actually deletes the object.

This is necessary in Lua, for example, because the object has a metatable
(which is, in fact, where the __gc metamethod lives.) Despite the fact that
the object is unreachable, its metatable and its finalisation method must
be marked, and so must everything which is reachable from them. Otherwise,
if the object contained the last reference to its metatable, and
consequently to its __gc metamethod, those might vanish before they were
run, or something that the __gc metamethod needed might vanish.

A __gc metamethod can be an arbitrary Lua function or lua_CFunction, which
is passed the finalisable object as an argument. In either case, it could
"resurrect" the finalisable object by, for example, placing a reference to
it in some table, even perhaps the globals table. There is no obvious way
to prevent this from happening, so the garbage collector must wait until
the next cycle to verify that the object is still unreachable.

As a consequence of this, not only will the object remain undeleted for (at
least) one more cycle, so will its metatable and everything reachable from
its metatable. If, for example, the metatable is used to store a Lua
counterpart of a userdata, then that counterpart will not get deleted until
the second garbage collection cycle.

I believe that Lua clears finalised userdata from weak-valued tables but
not from weak-keyed tables. It is debatable whether that is correct
behaviour or not; it depends on how you think about weak tables.
"Resurrection" may be poor programming style, but there are occasions for
it, as with everything; if an object were resurrected, it might or might
not be desirable to retain it in a weak table. Lua's rule is probably
reasonable: it is more likely that one would want it to hang about in a
weak-keyed table than a weak-valued table; but it could also be argued that
it should stick around in both until it is really and truly garbage
collected (if it is not resurrected).

Another consequence of this is that if two finalisable objects refer to
each other (say through links in their respective metatables), neither will
ever get reaped. To prevent this, Lua (and most finalising GC's) forcibly
reaps all objects at exit. This is certainly the only reasonable thing to
do, but it can cause finalisation methods to blow up because of dangling
references. It is, therefore, always advisable to be extremely paranoid in
finalisation methods; in general, a finalisation method should not attempt
to use any object which might have been created after the object being
finalised, and if it must do so, it should make an attempt to verify that
the object still exists.