Destroyed physics objects don't immediately release Lua objects as expected

Issue #1273 resolved
created an issue
  1. Run the example program
  2. Press a key to create a physics world with 6MB of userdata
  3. Press a key again to destroy the world, collect garbage, and recreate the world

After recreating the world, there should be only one such world in memory, but Lua reports two worlds' worth of memory used. The old world's userdata was not collected.

Manually clearing the userdata avoids the issue. (In this example, doing more garbage collection also helps, but that may not be enough for a major project.)

Comments (7)

  1. Raidho

    Well it appears there is in fact nothing out of order. This is how Lua registry index functions. Userdata is kept in Lua by means of creating a strong reference to it in that table, this is what setUserData function does. When you pass nil into it, reference is cleared. When object is destroyed, this reference is also cleared. If that was the only reference to userdata, then it becomes garbage and will eventually be collected. And this is exactly what happens.

    I however failed to figure out why calling reference clear manually marks the object as garbage immediately, but with automatic clear it puts it into backlist from which it's only "promoted" to garbage during sweep, and is removed during next sweep. It's like this even if the userdata setter function for nil as parameter does absolutely nothing but removing the reference. Oddly enough, if you pass no arguments, it doesn't instantly puts userdata to garbage either.

    @Bart van Strien no, it behaves like this for any userdata.

  2. Bart van Strien

    .. I may have confused myself a bit there. What I meant to say was that it's possible this is because during the first pass the __gc metamethod gets called, which in turn releases the reference to the userdata. Before that point, the user data has a reference. Depending on how the garbage collector is implemented, it may not pick up on the user data becoming garbage during the iterations, and thus it might only be collected the next iteration.

    That's all just a guess though, I'm not familiar with the internals of the luajit (or puc lua) garbage collector.

  3. Raidho

    I have a hunch, that this is due to timing. If you don't remove the reference manually, it will be removed in destructor code envoked by GC - reference clear doesn't happen immediately when you call destroy, it's only called when the object is already being processed by GC. Maybe in this process it bumps up abandoned data from white to gray color, resulting in it staying for one extra cycle.

  4. Raidho

    Yep, sure enough, putting udata->ref->unref ( ); in ::destroy solves the problem. Since destroyed body is unusable anyway, clearing the reference early doesn't detract from anything.

    If there's green light for this, I'll patch up the Reference class in to be less wasteful general and all the according physics library functions that use it. You'll have to give it a little while though, searching for culprit I've tore the source code to shreds and sewn back together haphazardly, so I'll have to write that from scratch.

  5. Bart van Strien

    Fix Issue #1273

    Added Lua index registry clear routine to destroy method of classes that use it. If this is left to GC, the abandoned userdata would stay in memory for one full extra cycle.

    Prettified the code slightly. Clarified some comments. Refactored Fixture class udata name. Removed Reference class include from Shape (it doesn't use it).

    → <<cset 33511fb56ffe>>

  6. Log in to comment