Memory Management in Monkey?

Monkey Forums/Monkey Programming/Memory Management in Monkey?

Hima(Posted 2011) [#1]
I was wondering how Monkey handle memory management for each language. What's the best practice and what are the "gotchas" I should know?

I'm working on a bullet-hell shooter targeting android and iPhone, so memory management is really important.

Thank you in advance!


Xaron(Posted 2011) [#2]
Best is to avoid create new instances ("New") as much as you can during run time. Preload everything you need. As far as I know Monkey uses some kind of garbage collector.


Hima(Posted 2011) [#3]
I would like to know how that garbage collector work exactly. How is monkey dealing with this once it's translated from monkey to, say, ObjC?


Xaron(Posted 2011) [#4]
When I look into the native code I see plenty of gc_collect() calls (e.g. in the iOS ObjC target code).

I don't know if you can configure it or disable it to call the garbage collector manually...

And how it works... well, just look into modules/monkey/native/lang.cpp for instance. It looks like a straight forward reference count.


Canardian(Posted 2011) [#5]
I guess gc_collect() is a abtraction function for GC, which uses only reference counting in languages which don't have GC, and uses the language's real GC for languages which have GC.


xzess(Posted 2011) [#6]
Well i have huge problems with memory, i even tried preload everything on start with lists and then move from list to list

If anybody knows a good workaround i would be really happy

Please try to see:
http://www.xzess.org/Webgames/Exhale/


slenkar(Posted 2011) [#7]
are you recycling bullet objects?


AndyGFX(Posted 2011) [#8]
Main question is, what have you moved to other list, pointer/instance or copy of list item.


marksibly(Posted 2011) [#9]
Hi,

The main 'gotcha' is that C++ GC is only invoked after OnCreate/OnUpdate/OnRender return control back to the OS.

Also, there are currently bugs in the recycling of graphics/sounds, so if you've got lots of image/sound loading going on, that may be causing leaks too.

'Moving things from list to list' *WILL* invoke new a lot (a new node is allocated per list insertion) so I wouldn't recommend this. But I also wouldn't recommend avoiding new entirely - as always, find out what's going wrong before fixing it, and don't rely too much on 'accepted wisdom' as every app is different.

Just what issues are you seeing?

If the app runs for a while before running out of memory, it's highly likely to be a memory/resource leak of some kind since GC is performed every OnUpdate/OnRender.


Dima(Posted 2011) [#10]
New() is the biggest bottleneck when it comes to performance. You want to pre-allocate as much as possible, and reuse instances whenever possible. There is nothing wrong with Lists for some stuff - if you only need to fill the List once and not constantly generate New() nodes. Removing nodes creates Garbage and will invoke the CG at some point, which is unknown exactly when. Lists are easy to handle though and can support any number of nodes.

One of the tricks I use is a simple array.

For the sake of simplicity let's assume the array is of fixed length (although resizing arrays is pretty straight forward). Say you declare a bullet array of size 1000, and at load time you create a New() instance for each of those 1000 bullets; now you have an array of pre-allocated objects sitting in memory, and GC is sleepy because the array points to all those instances.

Next you will need a variable (ActiveCount) to store how many active bullets are on screen, which starts at 0, because you haven't fired any yet. Every time you fire a bullet you increase the active count by 1 and fill out the bullets properties to reflect the actual projectile.

Straight forward so far - you loop from 0 to ActiveCount and update/draw your stuff, so if there are 3 bullets on screen you only loop through those, and the other 997 are idle. If you run out of room in the array you can simply resize the array (double the size is standard), this will cause a hickup because of New(), but this is beside the point.

My little trick resides in Removing objects from that array. Unlike a stack (which this sounds very similar to), i don't want to move the rest of the stack down when a random element is removed, I want to keep my array fixed! Therefore, when you think of adding/removing anything from any data structure, you almost always deal with one element at a time (forget SIMD or parallelism, its outside of Monkey's scope). When I remove a bullet, i don't want to set that array[index] to Null, because the object is lost and GC wins. I also don't want to just mark is as 'Inactive' because then I'm looping through redundant data.

What I do with Removes is SWAP PLACES with the last ACTIVE element from that array and reduce ActiveCount. Done!

Say I have 10 bullets on screen, and bullet #3 goes off screen, now there should be 9 bullets left. I take bullet #10 (last active), and swap pointers with #3, so bullet 10 is located where 3 used to be in the array, and of course ActiveCount is reduced by 1. Now i have 9 active bullets, and the one that just got killed is simply moved up to ABOVE ActiveCount. This way you never have to loop through redundant in-active data, and inserting/removing objects does not invoke the GC or memalloc.

i probably didn't have to write so much to explain something simple as this, but maybe someone else will find it useful. Also, this is only useful if the order of elements doesn't matter, like all sorts of bullets flying around.

To sum it up: using pre-allocated arrays and avoiding New() and GC, performance can be increased dramatically.

Add() - increase ActiveCount by 1, fill out last active slot with data
Remove() - swap last active element with the one being removed and decrease ActiveCount by 1
Update() - just loop from 0 to ActiveCount-1

I apologize in advance for any mistakes in my post no time to proof read, and for writing so much about something so simple.


Samah(Posted 2011) [#11]
New() is the biggest bottleneck when it comes to performance. You want to pre-allocate as much as possible, and reuse instances whenever possible. There is nothing wrong with Lists for some stuff - if you only need to fill the List once and not constantly generate New() nodes. Removing nodes creates Garbage and will invoke the CG at some point, which is unknown exactly when. Lists are easy to handle though and can support any number of nodes.

Diddy's ArrayList uses a dynamically-sized array rather than nodes.

I'm thinking of implementing an ArraySet class using your pointer-swapping trick.


marksibly(Posted 2011) [#12]
Hi,

> What I do with Removes is SWAP PLACES with the last ACTIVE element from that array and reduce ActiveCount. Done!

Nice trick - that's what we used to do in the Apple][ days!


xzess(Posted 2011) [#13]
Thanks for your help!
I guess it's very the same I am doing:

Oncreate I preload all images, put all class pointers (only one img pointer) in a list preloadedimages and then I only remove and add the last item to a list called activeimages when needed, if I don't need it anymore I move it back and set all references to NULL

I limited everything so that there is a maximum of 1012 images at the same time (1000 ships with 8kb filesize each
Still not working

But I will try it with the array instead of nodes

Thanks


marksibly(Posted 2011) [#14]
Hi,

1012 images?!?

Are you actually calling LoadImage 1012 times?

If so, that's a lot of graphics memory and you might want to consider using 'sprite sheets'...

Or are you just drawing the same image 1012 times?


therevills(Posted 2011) [#15]
Nice trick - that's what we used to do in the Apple][ days!


Coding for mobile devices is quite old skool ;)

The old tricks are getting a good work!


dopeyrulz(Posted 2011) [#16]
Dima,

Nice explanation! Do you any issues with rendering order of objects? I guess if you use your lists for specific objects this may not be an issue.


marksibly(Posted 2011) [#17]
Hi,

Here's a linked list approach - has same constant time but keeps actors sorted (untested!):




xzess(Posted 2011) [#18]
Yes mark, im drawing the same image about 1000 times

Thanks, i will give it a try


marksibly(Posted 2011) [#19]
Hi,

Also, a double buffered approach...




dopeyrulz(Posted 2011) [#20]
Great - thanks Mark! Might see you I can use these in my project.