- Introduction to lua
- Basic Setup
- Estrela for Luxinia
- Scenegraphs
- Scene Setup
- Models/Meshes
- Input handling
- Basic physics
- Physics surface properties
- Vehicle physics
- Using models
- Picking
- A Simple UDP Server
- GUI for the UDP Server
- Tetris attack clone prototype
- Stencil Shadows
- Sprite animation with matobject
- Project archives
- Estrela for Cg
- Specular mapping shader
- Material and Matobject
Object management in Luxinia .97
In the transition from .96 to .97, we chose, to enable the garbage collection for any kind of data, with the result that objects are deleted, once they are no longer refered.
This has some advantatages but also disadvantages. The main advantage is, that reloading code is simple, since the GC (Garbage Collector) takes care of all lost references. However, we need now to keep track of all our references as long as we want them to keep. Our experience was, that we normaly keep a structure in lua anyway in order to interact with everything, so this is the normal case anyway.
If we lose the reference of an object (like an l3dnode or actornode), the visible object will disappear eventually, if the garbage collector calls the destructor of the actornode:
function makeactor () -- actor is the global variable - it will be -- overwritten, so the old actor will be collected actor = actornode.new("gc",0,20,0) actor.l3d = l3dprimitive.newbox("gc",1,1,1) actor.l3d:linkinterface(actor) end for i=1,100 do makeactor() end collectgarbage "collect" print(actornode.getcount()) -- there's only one active actor
The box will be visible for some time until it is collected. If you want to directly delete it (and not depend on when gc collects stuff), just call :delete as usual.
If we have a file that initializes various global variables, we can easily reload this anytime without too much troubles.
Pitfalls
There are two cases that are problematic:
- Luxinia crashes
- Something is not collected though we think it should
Crashes
The first case can happen if a proper cleanup is absolutly necessary. We did not try to make luxinia foolproof and there are things that are simply evil and will make the engine crash, though it is quite unlikely for nearly all simple situations. In most cases, the rendering system is the cause of these troubles, since this is the part which is most hardware specific and should run the fastest. However, this only means that those (few) instructions that cause the crash must make a proper cleanup, i.e. by making sure that the previous setup is deleted properly.Most of the crashes would be related to resources not being available, like when resourcechunks are used and managed manually. Then you must be sure that the resources in a cleared chunk are not used anymore.
Memory leaks
In some cases, lua variables are not freed by the GC. I've never seen a case where its been the fault of the GC - its always been by my mistake that a variable could not be freed.Any variable that is reachable from the global context will not be collected - finding the references can be tricky. Examples:
function foo() local variable = actornode.new("") -- will be collected -- if the function returns end function foo2() variable = actornode.new("") -- will not be collected -- if the function returns - unless we set -- _G.variable = nil end function foo3 () local variable = actornode.new("") -- will not be collected function bar () print(variable) -- bar is refering variable and -- _G.bar is a global function, so variable CANNOT -- be collected, as bar needs it end end function foo4() local variable = actornode.new("") -- will be collected local function bar() print(variable) end bar() -- bar is used only inside foo4 - it'll be also -- gced
The foo3 case is a quite tricky example, which can be tough to trace.
Tracking references
It is very complex to trace references where it is kept.First, we should find out which variables are not gced.
A quite simple way to find out this, is to put the references that should be collected in a weak table and see, if they are collected:
tracker = {}
setmetatable(tracker,{__mode='v'})
function addtrack (key,var)
tracker[key] = var
end
function printungced ()
collectgarbage("collect")
for i,v in pairs(tracker)
print(i,v)
end
end
If we call printungced, we will get a list of all variables that have not been collected yet.
If we know, which references are not collected, we can take a closer look in the code:
- Make sure it is not declared global somewhere
- Make sure it is not kept in a list which is declared global
- Make sure that there exists no function that is still refering to the local variable. Functions may refer to global values however, as these are not upvalues of the function.
- Make sure that the timers / timertasks are no longer refering your functions
