Optimizations for System Memory usage?

Also if you’re going to create the meters, do it right away.  No need to wait.

That said, waiting shouldn’t be a problem.

Why are you worried about this?  Are you running out of memory?

Whatever the case, there are no ‘actions’ specifically that increase memory usage. 

The ways to use memory and create leaks are myriad. 

The ways to avoid this are best know practices you can read about in the guides and will learn about over time through the tried and true technique of ‘making mistakes’.

Honestly, I never worry about this.  If, at or near the end of development, I suspect a memory issue, I measure my memory usage to see if my suspicions are true.

If you use SSK, you can easily measure memory usage at any time. with this line of code:

ssk.meters.create\_mem(true)

Note: If you want to know how much memory SSK is using, you can initialize it with measure set to true, then look in the console:

\_G.ssk.init( { measure = true } ) 

This is the console output:

 -------------------------------------------------------------------------- -- Initalizing SSK -------------------------------------------------------------------------- ssk2.core : 11 KB : 2 globals added. ssk2.extensions.display : 2 KB ssk2.extensions.io : 7 KB ssk2.extensions.math : 7 KB ssk2.extensions.string : 16 KB ssk2.extensions.table : 23 KB ssk2.extensions.timer2 : 4 KB ssk2.extensions.transition : 3 KB ssk2.system : 11 KB ssk2.colors : 16 KB ssk2.display : 42 KB ssk2.math2d : 30 KB ssk2.cc : 6 KB ssk2.actions.actions : 23 KB ssk2.easyIFC : 132 KB ssk2.easyInputs : 72 KB ssk2.easyCamera : 15 KB ssk2.misc : 52 KB ssk2.pex : 18 KB ssk2.dialogs.basic : 9 KB ssk2.dialogs.custom : 5 KB ssk2.factoryMgr : 4 KB ssk2.vScroller : 9 KB ssk2.behaviors : 9 KB ssk2.android : 1 KB ssk2.security : 9 KB ssk2.persist : 5 KB ssk2.points : 4 KB ssk2.soundMgr : 23 KB ssk2.easySocial : 2 KB ssk2.shuffleBag : 4 KB ssk2.meters : 7 KB ssk2.files : 46 KB ssk2.tiledLoader : 31 KB : 1 globals added. ssk2.easyPositioner : 15 KB ssk2.easyBench : 20 KB ssk2.external.proxy : 1 KB ssk2.external.wait : 2 KB ssk2.external.randomlua : 7 KB ssk2.external.30log : 13 KB ssk2.external.portableRandom : 4 KB : 1 globals added. ssk2.external.ponyfont : 13 KB ssk2.external.global\_lock : 4 KB : 4 globals added. -------------------------------------------------------------------------- SSK Total : 738.32 KB -------------------------------------------------------------------------- Exporting the SSK 2 feature 'core' added 26 globals. Exporting the SSK 2 feature 'colors' added 28 globals. Exporting the SSK 2 feature 'system' added 37 globals. -------------------------------------------------------------------------- SSK 2 Added: 101 globals. --------------------------------------------------------------------------

I’m telling you about SSK’s own memory usage measurement for two reasons:

  1. If it seems like too much and you’re not using part of SSK, you can take that part out, but see the memory usage first to decide if it is worth it.

  2. You can borrow and/or modify my memory measuring technique.  In a nutshell, I measure memory usage before an action that will increase memory usage, then I capture the delta and print it later. 

Have you checked if your continually increased memory usage originates from creating new objects or is there a memory leak, because it sounds like you have a memory leak.

Some basic things that you can do:

  1. keep using locals and avoid globals. Also, get rid locals once they are no longer used, i.e. pay attention to scope

  2. if you are placing new objects in tables, consider clearing up those tables at times and reusing old table entries instead of just creating new entries that bloat the tables.

  3. for texture memory, prefer using image sheets instead of individual images

 

It sounds like you’re describing a memory leak. How are you measuring your memory? Growing by 1MB every 5 minutes seems kind of large for system memory, perhaps that’s not much for texture memory. Also, I’m not sure how to measure memory used by audio. This is why it’s important to know how you’re measuring your memory.

Are you perhaps loading audio and not disposing of it?

Rob

@d.mach,

Missed Your Problem

In my answer above, I asked why your were asking.  This might seems stupid, but you see I didn’t see your reason on my first browse of your post.

The reason I missed the problem is because your post was hard (for me) to read.  I immediately tune out when OPs fail to use capitalization, spelling, full sentences, and paragraphs.  

While I answer many posts, I admit I am very bad and reading OPs and very very lazy about it.  

My advice on posts is as follows:

  • When asking for help, put the problem first: “memory is increasing about 1MB every 5 minutes”
  • After you get done writing the post, read it and correct anything that would make your English teacher give you a bad grade.
  • Read it again and make it shorter.
  • Post it.
  • Read it again to be sure the post didn’t get hosed up.

When I post a question I try to minimize the effort of readers and those who answer. i.e. I should spend more time asking the question than they have to spend reading and answering it.

Your Problem

If you are getting an increase in memory of 1MB every 5 minutes you have a leak.

You are in all likelihood:

A. Creating objects and not deleting them.

-OR-

B. Creating objects, deleting them, but not clearing the references to them.  Thus they cannot get garbage collected.

99% of leaks come from people not understanding:

  • Scope and Visibility
  • How Corona objects get garbage collected
  • The impact of using composer.* and how/when scenes get cleaned/cleared.

After that, you might be doing something silly with Lua tables, but that is something a person would have to see your code for to debug.

Thank you guys for the fast help! Much appreciated!

I wonder if a table of my enemies can be the problem because I still count it up when new enemies are created. I delete old (destroyed) enemies which are first in the table after a while. Here is an example:

enemyarray={enemy1,enemy2,enemy3}

then when a new enemy is created I do: enemyarray[#enemyarray+1]=enemy4 and so on…

Then after a while (when I’m sure the first enemies are dead I remove the enemies like this:

display.remove(enemy[1])

display.remove(enemy[2])

enemy[1]=nil

enemy[2]=nil

But this means the table still is growing, when I add new enemies, right?

Could this be the problem regarding memory?

One more thing: If the code is correct above, can I do something like this when an enemy is getting deleted:

display.remove(enemy[1])

then creating a complete new table and adding the still existing enemies to the new table and remove the old one completely.

local newtable={}

for x=2,#enemyarray do

       newtable[x-1]=enemyarray[x]    

       enemyarray[x] =nil

end

enemyarray=nil

enemyarray=newtable

newtable=nil

Can this keep the enemy array down in size and be helpful? Do I miss something here regarding the code, like for example CAN THIS EVEN BE DONE while the enemies are still on screen and the level is in high action?

The audio in the game is preloaded in e module and then just called by audio.play().

I’m not loading any audio files during the gameplay.

I’m using this code to measure the memory:

function printMemUsage()           local memUsed = (collectgarbage("count")) / 1000     local texUsed = system.getInfo( "textureMemoryUsed" ) / 1000000          print("\n---------MEMORY USAGE INFORMATION---------")     print("System Memory Used:", string.format("%.03f", memUsed), "Mb")     print("Texture Memory Used:", string.format("%.03f", texUsed), "Mb")     print ("Transition Stash Count: "..#transitionStash)     print ("Timer Stash Count: "..#timerStash)     print("------------------------------------------\n")           return true end if isSimulator==true then     Runtime:addEventListener("enterFrame",printMemUsage) end

Do Not Use Numeric Indexes

I say this all the time, but nobody listens to me. 

Do not use numerically indexed tables to track game objects. 

enemyarray[1] = enemy1

Use the objects as the index instead:

enemyarray[enemy1] = enemy1

It is simpler, and not going to make any impact on iteration-speed for reasonable numbers of objects.

Best Known Method

This is the BEST way I know of to create objects that are in a list and have them manage their own reference in the list.

local enemies = {} -- the list of enemies -- A common/shared finalize method to remove an enemy object from the enemies list. local function enemyFinalize( self ) enemies[self] = nil end -- A enemy builder function local makeEnemy( group, x, y ) -- make it local obj = display.new.... group:insert( enemy ) -- put it in the 'list' enemies[obj] = obj -- add and set up finalize listener obj.finalize = enemyFinalize obj:addEventListener("finalize") -- return reference in case you need it return obj end

Now, you can make enemies with your builder:

makeEnemy( 100, 100 )

How Do I Iterate Over The Table?

for \_, enemy in pairs( enemies ) do -- do whatever you need to with enemies here print( enemy.x, enemy.y) end

How Do I Clean Up When Deleting Enemies?

You don’t need to.  Just delete the enemy.

For example, this look will delete all enemies in the table and they will all clean up their own entry:

for \_, enemy in pairs( enemies ) do display.remove( enemy ) -- Tip: never use obj:removeSelf() end

How Do I Count Entries In The Table?

Maybe you need to count entries are are confused about how.

Just add this code in main.lua (or elsewhere before you need to call it)

-- == -- table.count( src ) - Counts all entries in table (non-recursive) -- == function table.count( src ) local count = 0 if( not src ) then return count end for k,v in pairs(src) do count = count + 1 end return count end

Now, you can count the enemies table like this:

print("Num enemies == ", table.count( enemies ) )

Thanks for the info. I tried your approach once but was still using the numerically indexed version because I need the info which enemy was created first (added first to the table) because this one is the one which will be targeted next by a weapon in case an enemy is destroyed.

With all the details from your last entry I will give it another try and figure out how to keep the info which enemy was added next somewhere else to look it up.

If you need that, just create a counter and add the count as a field to the enemy.  

local enemyCounter = 0 -- A enemy builder function local makeEnemy( group, x, y ) -- make it local obj = display.new.... group:insert( enemy ) obj.num = enemyCounter enemyCounter = enemyCounter + 1 ... more, but important bit is above

 
 
Then iterate over the list to find the enemy with the lowest id.

local function findNextEnemy( t ) local lowest = math.huge local target for \_, enemy in pairs(t) if( enemy.num \< lowest ) then target = enemy lowest = enemy.num end end return target end



local nextEnemy = findNextEnemy( enemies ) if( nextEnemy ~= nil ) then ... do something end

AWESOME! Thank you very much for your help!

One more thing for understanding my code from above better: could this work (while not perfect)? Or is it a bad idea to create a new table, fill it and copy it to the old one?

Just asking to learn something here. :slight_smile:

I don’t know what you mean by this statement, ‘copy it to the old one’

When I read that I see this in my mind:

local oldone = {} local doit() local newone = {} for i = 1, 1000 do local obj = ... newone[obj]=obj end -- the copy for k,v in pairs(newone) do newone[k] = v end end&nbsp;

, but then maybe you mean this?

local oldone = {} local doit() local newone = {} for i = 1, 1000 do local obj = ... newone[obj]=obj end -- the copy oldone = newone end&nbsp;

Would I do the prior?  No.
 
The latter?  Not really, but that is way better than the prior.

To continue with copying tables, you might want to read http://lua-users.org/wiki/CopyTable.

For instance, here’s a common mistake when “copying” tables in Lua.

 

local tableA = {} for i = 1, 10 do tableA[i] = i end local tableB = tableA tableB[2] = nil print(tableA[2])

What this does is create tableA and fill it with 10 integers. If you then simply state that “local tableB = tableA”, then these two tables both refer to tableA, i.e. tableB is not separate from tableA. As you can see, setting tableB[2] = nil will set tableA[2] to nil as well.

If you didn’t know about this before, then now you do.

Thanks for letting me know about your SSK! I’m just testing it to show the memory with SSK instead the code I showed here.

Now I have one problem: After using the SSK I get runtime errors for different objects or parameters in the game.

The memory which was shown was okay and stable compared to the mem I had with “my” code.

You’re welcome, but I don’t understand this statement:
 

 After using the SSK I get runtime errors for different objects or parameters in the game.
The memory which was shown was okay and stable compared to the mem I had with “my” code.

  1. What exact runtime errors.

  2. Sounds introducing SSK into your project has simply exacerbated the memory problem. i.e. The problem already existed, but is now more easily encountered.

  3. If you just want to use my memory meter it can be extracted from SSK as a standalone module: meters.lua

Using

require "ssk2.loadSSK" \_G.ssk.init()

in my main.lua will result in runtime errors during the runtime of the game. And there is nothing to see why this is happening because it is because of graphics and parameters I use in my code which work fine when I not require the SSK.

I have used the ssk.meters.create_mem(true) in my level code and this was showing stable numbers of mem usage. The following code (I have used before):

local memUsed local texUsed local&nbsp; printMemUsage = function ()&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; memUsed = (collectgarbage("count")) / 1000 &nbsp;&nbsp;&nbsp; texUsed = system.getInfo( "textureMemoryUsed" ) / 1000000 &nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; print("\n---------MEMORY USAGE INFORMATION---------") &nbsp;&nbsp;&nbsp; print("System Memory Used:", string.format("%.03f", memUsed), "Mb") &nbsp;&nbsp;&nbsp; print("Texture Memory Used:", string.format("%.03f", texUsed), "Mb") &nbsp;&nbsp;&nbsp; --print ("Transition Stash Count: "..#transitionStash) &nbsp;&nbsp;&nbsp; --print ("Timer Stash Count: "..#timerStash) &nbsp;&nbsp;&nbsp; print("------------------------------------------\n") &nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; return true end if \_G.isSimulator==true then &nbsp;&nbsp;&nbsp; Runtime:addEventListener("enterFrame",printMemUsage) end

This code was growing the memory steadily and after a while it falls back to the starting value and starts growing again. BUT the memory (system) was not falling back to the starting value exactly with the starting value growing also over time.

Unfortunately I was not able to run your SSK for long because of the Runtime errors preventing the game to be played longer than a few minutes.

  1. I’m having trouble understanding how SSK can change your game’s behavior and create invisible runtime errors (no error message?)

I’d love to see this. Please send me your game if you feel comfortable:

rgemail.png

  1. It is natural for memory usage to increase over time then fall back to a smaller value.  That is how memory management works in Lua.

The memory is not instantly released when you delete objects.  When Lua is getting close to ‘running out of memory’ it purges all freed slots at the same time first to see if it actually needs to request more memory.

i.e. This shape  (ignore rest of image; just the shape is important) is what you would see if you were graphing memory of a running game that did a lot of creating and destroying of objects:

java-memory-usage-example.png

Q: Where are you initializing SSK?

It should be in main.lua before almost all other code.

I now have moved the init and require to the start of main.lua because I had it a little bit to late.

Now I get this error when using ssk.meters.create_mem(true) later in my code (when a level is started)

…/ssk2/extensions/timer2.lua:63: attempt to index local ‘newTimer’ (a nil value)

I’ll have to see that in context to help.  See email address above.

Also, you have the code so adding print statements to debug should be easy.  

My guess is you’re including other libraries or replacing functionality somewhere and it is conflicting with SSK and in this case the external lib SSk2 includes timer2

– OR – 

possibly… timer2 is catching a bug in your code that timer didn’t.