Memory

Different compilers and runtimes have different rules about how scripts are executed/interpreted. The following comments are based on typical compiler and runtime behaviors.

In LSL, each script runs in its own separate 16KB (64KB in Mono) section of simulator memory called a "sandbox". In theory, scripts are not allowed to manipulate other script's or the simulator's data stored in memory (they are kept in their own "sandboxes").

There are 3 areas of a script's memory space:

stack: Local variables and other bookkeeping information (discussed here) are stored in the stack. When a function

X

is called, the following happens: (1) the location of the statement of the calling function (the one that calls X) is pushed onto the stack, (2) as function X is executed, any variables declared within X (these are called local variables) are pushed (saved) onto the stack, and finally, (3) when function X ends its execution, any value that it returns is pushed onto the stack. To return to the next statement of the calling function (i.e., the statement after the one that calls X), the stack already contains that information (see #1), and if any value is returned from X, the stack contains that, too (see #3). When the calling function resumes, the stack will be restored to the way it was before function X was called.

heap: A heap is more or less what it sounds like: a "pile" of data. The contents of strings, keys and lists are all stored on the stack.

statics: This is where all the script's global variables are stored.

bytecode: LSL is compiled into bytecode which is what is executed to run the script. This also resides within the 16KB of free space for the script.

16KB (64KB in Mono) = stack + heap + bytecode

High Memory:

Low Memory:

The (Bytecode + Statics) space is fixed. The Stack and Heap are not fixed: the Stack grows downwards towards the Heap space, and the Heap grows upwards towards the stack.

Stack-Heap Collision Errors

When the script starts, data is pushed onto the stack starting at the High Memory location and downwards. Every time you create a string, list or key, heap space is allocated, and if there isn't enough space the heap is expanded. This means that as more and more data is pushed onto the stack (e.g., more and more function calls are nested or lots of local variables are declared), and more and more data is stored in the heap there's a danger that the stack will run into the heap! That is, the stack's data will start to write over the heap data. Fortunately, this condition is reported by the system as a Stack-Heap Collision Error.

To regulate the amount of memory you use, remember the following:

1. Don't keep large lists and strings hanging around once you're done with them. Note that, even if they're not going to be used again, the contents of all local variables stay hanging around until they're overwritten or the function returns.

2. Reduce the amount of nesting of function calls. The more nested your calls are (e.g., A calls B, which calls C, which calls D, etc...), the more memory is retained in the stack. Your trade-off will be between writing long, flat functions, or splitting a function into subfunctions so that code is more readable and reusable.

3. Consolidate your variables, the tighter your code and the fewer the variables used, the less memory required. Only use intermediate variables while you write the code and remove as many as you can once you have the code working. By using general global buffers you can save memory but at the expense of readability, not to mention you have to be very aware of who writes to them and when.

4. Pay attention to what you pass into a function or assign to a variable. Variables are passed by value to a function, so they are copied when you call a function. The deeper the number of calls passing the same variable, the greater the number of copies. Pass only what you need. If possible, use shorthand to reduce the amount of data passed (e.g., instead of passing a string "5094", try passing an integer 5094). Also, when you assign an expression or variable to another variable, you are actually copying its data to the variable you are assigning it to.

5. When you use the same data, hold it in a global variable instead of repeating it in place in your functions. For example, if you use the string "Hello there" multiple times in your code, store it in a global (string HELLO_THERE = "Hello there";) and refer to it. This will create just one copy of the data in the heap. (Some compilers are smart about this.)

6. Understand how much memory is consumed by each type of variable. For example, Lists use alot of memory. Sometimes it's possible to use a string instead (also see CSV).

7. If you run into memory limitations in a script, create another script and pass information between them using link messages.

8. Recursive functions. First, watch out for infinite recursion (a function that calls itself forever). Since a recursive function eats up stack space, transform your recursive function into an iterative function. You can usually do it (check the web for examples on recursive functions).

9. Function calls are expensive, if you can reduce the number of them in your code, it will save you a large chunk of memory.

10. Don't expect the compiler to implicitly typecast your constant integers to floats at compile time; it won't (except for globals). You can save memory by writing your constant that are being stored into floats, vectors or rotations as proper floats.

A script's historic memory usage can be determined by using llGetFreeMemory.