Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Leaky LSL string buffer in functions?

Fenrir Reitveld
Crazy? Don't mind if I do
Join date: 20 Apr 2005
Posts: 459
01-02-2006 18:05
Well, I'm working on a large scripted system and I ran into a rather nasty issue. I'm not sure if this is any true malfunction with the LSL engine, or if it's just The Way It Works.

My program is broken into about 8 different sub-modules. These all handle various parts of my program flow, such as reading configuration files, storing commonly-accessed data types, and so on. Well, I started noticing that one of my data storage scripts was leaking memory like a sieve. Upon every iteration of script states (from its "waiting for commands" to "go reload your config file" part), it was leaking around 1k!!

I know that llGetFreeMemory() is supposed (?) to return a low water mark, but I was clearing the only list that I use in the script every iternation. So I should have not ended up less free memory at the starting point every time around.

Further investigation found that my log function call was causing the memory leak! Here it is:

CODE
log (string msg)
{
llMessageLinked (LINK_SET, 8000, msg, "") ;
}


Now, that's just a convienence function, so I don't have to look at my script and go, "Uhh, what was listening for 8000 link messages?" I was willing to waste the heap space necessary for sending a string to a function, but I should have recovered that upon the function's return. Losing 1k for just a few log() calls didn't make any sense to me at all.

Long story short and many tests later, I found out that if you call a llSay(), llWhisper, llShout() or llMessageLinked() from a function -- the temporary string that appearently gets allocated for sending the message is never returned to the script heap pool! I know it's the string, because this code test shows consistant memory usage:

CODE
// Function leak-test, by Fenrir Reitveld

integer i ;
integer ii ;

leak_test (string in)
{
llSay (8128319, in) ; // send it out into the ether...
//llSay (0, " strlen: " + (string)llStringLength (in)) ;
}

default
{
state_entry()
{
llWhisper (0, "-->>" + (string)llGetFreeMemory()) ;

jump skip ; // <-- uncomment to show random memory leak

string temp ;
for (ii=0; ii < 50; ii++)
temp += (string)"^" ;
leak_test (temp) ;

@skip;

for (i = 0; i < 5; i++)
{
llSay (0, " " + (string) (i) + "> " + (string)llGetFreeMemory()) ;
string out ;
integer ii ;
for (ii= 0; ii < llFrand (50); ii++)
out += (string)"^" ;
leak_test (out) ;

}

llWhisper (0, "<<--" + (string)llGetFreeMemory()) ;
}
}


If I just insert the llMessageLinked()s right into my code in the appropriate states and ditch the function, there is no memory leak. I have not seen any other memory leakage related to running functions, either.

I really don't know if is a bug or not, or perhaps due to how functions are handled (being "stateless";). So I'm not sure if this deserves a bug report. Thoughts? Suggestions?
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
01-02-2006 19:32
At first i thought you found a bug, but isn't not a leak, its just llGetFreeMemory being it's weird self. You see strings, keys and lists are actualy pointers, and a string allocated on the stack is just a 32bit pointer, and the string is in memory just like if the string had been a global or local variable. When you generate your string temporary results are pushed into the stack which in turn eats memory effecting the result of llGetFreeMemory()

The stack and variables are actualy related.

basicly how the LSL stack works
[Globals, Locals, Stack]
Global addresses work off the start of the stack.
Local addresses work off a pointer in the the stack. At the end of a scope these are popped off the stack.
Temporary stack values are pushed in and popped off the end of the stack (First in Last Out).
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.
- Cyril Connolly

Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.
- James Nachtwey
Fenrir Reitveld
Crazy? Don't mind if I do
Join date: 20 Apr 2005
Posts: 459
01-02-2006 21:34
Aha, I see now. See, I was expecting the string to be allocated at function call time, and then returned upon exit. In other words, I expected the memory allocation as reported by llGetFreeMemory() to remain static after that first function call because I assumed it was pass-by-value.

But it just sends a 4-byte pointer to the function and the allocation isn't made until needed inside the function, where it pulls from the stack.

Wow, lot of nice gotchas there wtih llGetFreeMemory(). :) I didn't catch the part in the wiki about variable data structures just being pointers.

Thanks, Strife, that clears that up. So, in the end, it's How Its Supposed To Be. (Though I wish there was a way to get a bit more accurate picture of what the script is using from the heap aside from metrics I keep myself.)
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
01-03-2006 03:08
in Mono we have a good chance of llGetFreeMemory actualy working properly (that is returning the free memory as apposed to returning a historical value).
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.
- Cyril Connolly

Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.
- James Nachtwey