multiple loops and Garbage Collector

BlitzMax Forums/BlitzMax Beginners Area/multiple loops and Garbage Collector

Grafos(Posted 2010) [#1]
Hi all!
I've read a lot of tutorials that deal with small games. I was wondering, what if you want to break-up the loading of levels\menus on a larger game?
The way I would go about it in pseudo code is:
while exit=0
  select level
     case 1
          load  level 1 stuff
          repeat
              level1 loop
          until level<>1 
     case 2
         load  level 2 stuff
         repeat
               level2 loop
         until level<>2
      ....and so on so forth
    end select
wend


This seems to work for me, but the garbage collector, doesn't seem to like it, as it doesn't seem to clear the memory when I move to another sub-loop, probably due to loading stuff while in the main loop.

Is there a way to force clear the memory? Should I structure my code differently, and if that's the case, how would I do it so that I don't need to load everything at once?


Jesse(Posted 2010) [#2]
If you are using the same variables and just loading them with the new data then it should be no problem. The garbage collector will remove the previous data although it might get kind slugish while it is doing that depending on how much data it has to clear and load.

But if you are using completely new variables then unless you are clearing the old variables manually you will have a problem that is of course if you are using either global variables and/or recycling objects and/or local variables are within scope.


Grafos(Posted 2010) [#3]
I am using completely new variables. Every level\menu is initialized through its own type. I tried manually setting the reference to null, even manually nullifying every field of the type, but the memory does not clear.

Does manual clearing of memory involve something more than just setting a variable to null?


Czar Flavius(Posted 2010) [#4]
When you set a variable to null, it's marked as available for the garbage collector, but isn't cleared immediately. Basically the collector cleans it out whenever it feels good and ready. It could be a few seconds after the variable is nulled. You say that the memory doesn't clear, what method are you using to test this? If the memory isn't getting freed after the program quits, there is definately a problem. The garbage collector has been tested frequently by users on the forum and, apart from the case of circular references, it's watertight, so I wouldn't worry about it too much.

About your level selection structure, it might be better to have a generic LoadLevel function or method, which is given the number of the level to load. This way you don't need to repeat lots of common variable names and there'll be less work in adding new levels. It depends a lot on the type of game and how you are storing your data though. For example, let's say you have the map stored in a 2d grid 100x100, eg grid:Int[100, 100] (or replace Int with whatever type you need). Rather than having a seperate grid variable for each select..case, you now just need one that is updated by the LoadLevel. You could store is as a global variable, or as a field of a "Game" type or something.

Edit: You could try using these functions. GCMemAlloced() - returns memory used by all objects, strings and arrays in bytes. GCCollect() - tells the garbage collector to execute now. It returns the number of bytes freed.


Grafos(Posted 2010) [#5]
Oh, after I quit the game the memory is cleared.
But during the game, if I go lets say from menu1 to menu2, menu1 is not cleared, even if I explicitly set it to null. So even if I return to menu1 the memory will keep adding up to gigabytes, until I terminate the game.
I was thinking that maybe because each level\menu is created under the while\wend loop (see pseudo code on 1st post) the Garbage collector considers it still on scope and doesn't clear it.

Thanks for the suggestion on structuring. The problem is, each level is slightly different to the other, that's why I didn't take that route. Maybe I could write a function and add stuff selectively with if\then statements,
but I'm not sure if that's even more cumbersome.

Btw, sorry if I make really weird suggestions, I am self taught and really bad at understanding other people's code, I usually rely purely on my logic and I'm pretty sure I don't always end up with the optimal solutions to a problem.


Czar Flavius(Posted 2010) [#6]
Gigabytes?!!!!!??? OK I'd really like to see some of your code to see just what you're storing that's so large :P Are they image files? Or just plain data?

I edited my post above at the same time you posted this one, with some garbage collector functions you might like to try.

It's a good idea to store your level format in files. It takes a bit of extra work to create and process the files, but lets you add new levels in the future without changing code, plus it keeps your code light if there is a lot of level data you are storing. If you have a naming standard, eg level 1 is stored in file "level1.dat", level 2 is "level2.dat" etc, then if LoadLevel takes an integer parameter level_number, it can open file "level" + level_num + ".dat" and get that level's unique data from the right file, with only one function.

Alternatively, you can keep the loading stuff in the code, but you should seperate them off into functions. For example, LoadLevel1(..), LoadLevel2(..). You can basically copy and paste the code from your case statements into the function. Pass any objects you need loading as parameters, and update them in the function. It could look something like this.



The big advantage to this method is that each function's memory is stored seperately from other functions and also the main code area. This means if the memory is not being freed after leaving PlayLevel (and ordering a manual garbage collect, which ideally we shouldn't need to) then we have a big problem!

If you are not already, you should program using Strict or SuperStrict mode. I just realised this could possibly be the problem - in original mode, the code generated is less efficient. You should use Strict all time, if you are not already doing so. :)


Dreamora(Posted 2010) [#7]
Use functions in that case there.

Your problem is likely that you have all on global level and on global level the GC works a bit different than it does with cleanly seperated scopes.

also, make sure you have Strict / SuperStrict at the top, otherwise you have no variable scope at all (so they will also never go out of scope and won't be cleaned unless you force them to be)


Grafos(Posted 2010) [#8]
Thank you both for you suggestions.
Each level is a separate type instance. There are about three
type of levels which inherit their common fields from a parent type.
I also use a lot of types inside types.

I could restructure everything with functions on the main loop, for the sake of neatness, but I don't think it would make a difference with the memory clearing problem, or am I missing something? Your code is cleaner, but the way I see it not that much different to mine (I did mention I suck at reading other people's code :P).

Forgot to also mention thatI had already used GCMemAlloced() and GCCollect(). That's actually how I knew that the Garbage Collector has been clearing too little memory. And I avoid using global variables, there are only a handful of them.

Having played with Blitz3D for a while, I chose not to use strict\superstrict when I started with Blitzmax, as I couldn't be bothered with declaring global and local variables. It seems there was more to "strict" than what I initially thought, so maybe that's the first thing I should check and see if it makes a difference.

However I did try to nullify stuff manually and it didn't make a difference. I couldn't access the nullified variables, but their memory wasn't freed.
Was I right to assume that because the level instances are created inside a loop, they won't be freed by the GC until I exit the loop=exit the game?


Dreamora(Posted 2010) [#9]
Lets check a few things:

1. Where do you know from it does not clear the objects? The process manager won't help you that gives you the size of the application pool, but not how much of that is used effectively at the time. The GC will not free the pool as that would be suicide (alloc and freeing of memory references to the OS is a very slow operation so the GC keeps a pool of unused memory if the system has memory free to allocate future object in a fast way - thats the base upon which managed environments build, otherwise they wouldn't work)

2. What happens if you strict the whole stuff. As mentioned, scoping and many other optimizations don't work without strict. BM basically "drops back to stupid mode" if you have not at least strict to allow (out of BM code view) slugish code to run at all


Grafos(Posted 2010) [#10]
1. I just check the task manager. Every time I enter a menu screen, the previous one should have gone out of scope and be the memory it uses should have been freed.

To illustrate, if I go from the main menu to the load player screen and then back to the main menu, the memory should have been the same as when the menu screen originally opened, but it isn't. If I keep entering menu screens and coming back, the task manager will show my exe consuming gigabytes of ram. Printing GCCollect shows that very little amounts of ram are freed.

2. I am in the process of converting everything to abide to "strict". That's a lot of locals I have to declare, the non strict mode lets you get away with A LOT of stuff. I hope it'll make a difference.


Dreamora(Posted 2010) [#11]
2. Actually it does not "let you get away", it completely cuts all these features from existance.
I'm unsure how far the GC is working in comparision to its "normal mode" actually


Czar Flavius(Posted 2010) [#12]
the memory will keep adding up to gigabytes, until I terminate the game.
?????????????????????????


Grafos(Posted 2010) [#13]
lol, did that shock you?


Czar Flavius(Posted 2010) [#14]
I'm not inclined to believe it, as if that's the case you probably have a much bigger problem on your hands, garbage collector or not..


Grafos(Posted 2010) [#15]
Why is it so hard to believe? Say you load 100 mb worth of stuff each time you start a level which is never cleared. Every time you reload the level, you'll add 100 mb until you'll pretty soon reach gbs.

My preliminary tests with strict show a lot of promise, but I still need to work out stuff that eeerm, do not work with it. I'll keep you posted.


Czar Flavius(Posted 2010) [#16]
100MB of what stuff? Graphics? Could we see an extract from your level loading code?


Grafos(Posted 2010) [#17]
I don't think you'll understand much, it's got many Greek stuff in it
and even I have a hard time understanding what I was doing sometimes.
:P
But yeah, it's highly unoptimized as I keep loading the same stuff over and over every time I use them.

I followed this excellent tutorial at the beginning

http://blitzmax.com/Community/posts.php?topic=59509

but the author did say that he kept loading stuff over and over just for clarity, so I'm sure the memory usage will come down to saner levels as soon as I optimize everything and start reusing media. But atm I'm only focusing on the issue at hand, getting the garbage collector to actually... collect the garbage and I think yours and Dreamoras suggestion to use strict hit the nail on the head.


Grafos(Posted 2010) [#18]
For any noob like myself checking out this thread due to similar memory issues:
Apart from using strict/superstrict, don't forget to build in release mode when checking the memory consumption.

My game now peaks at less than 200 mb which is about right, given the amount of graphics\sounds I'm using. Thanks everyone for helping out.