force GCCollect, good practice?
BlitzMax Forums/BlitzMax Beginners Area/force GCCollect, good practice?
| ||
i've read several places not to rely on destructors, because GC works kind of random. i am tempted though, to force a collect after an object has been Nulled - or would that defeat the whole concept of a collector and kill performance? not sure if i remember correctly, but does messing with GCCollect outside of the mainloop lead to memory leaks? |
| ||
i've read several places not to rely on destructors, because GC works kind of random. Places such as...? |
| ||
Since BlitzMax uses a garbage collection system to delete objects, don't rely on a destructor being called at any specific time; the Delete() Method (destructor) will be called whenever the “garbage collector” gets around to it. from http://www.alsbonsai.com/john/BlitzMax_OOP_Tutorial.pdf ...and some threads in this forum i think? |
| ||
That doesn't say GC is "kind of random". GC is automatically called every few seconds, as can be seen if you constantly draw GCMemAlloced() to screen during execution. If you do get that paranoid about it, you can turn off automatic garbage collection and call GCCollect manually. Personally, I wouldn't bother though. Oh, and anybody who says they've got memory leaks in their program most likely has bugs in their own code, rather than there being something wrong with Blitzmax's garbage collection. I don't recall any reported problems with it. |
| ||
sorry, my bad - i just realised what you were aiming at, and was looking for a thread i just saw about GC updating every 5000 ms or something... If you do get that paranoid about it... hehe, well said... guess i am a little paranoid, it was just convenient to also clear a 3d object in that destructor, which was why i wanted to make sure it got cleared... |
| ||
the bigger question is. Why would you need to call GCCollect? If you can answer that questions then maybe you can improve your code. I had the same questions with a particle system I wrote. I think it is important that you keep track of memory usage. Automatic Garbage Collection is not a magic bullet. |
| ||
You are worrying about things you dont have to... the 3D object will be garbage collected when the object that references is eaten by the GC. |
| ||
the 3D object will be garbage collected when the object that references is eaten by the GC. It will be garbage collected the next time the garbage collector is run after the last reference was removed. Which can potentially be never. |
| ||
I find that I need to force garbage collection when making use of local arrays. Example:SuperStrict Graphics 320, 200 Local Map:Float[,] = generate(1000, 1000) 'Local Map:Float[,] = GCgenerate(1000, 1000) While Not KeyHit(KEY_ESCAPE) If MouseHit(1) Then Print GCMemAlloced() Wend End 'Don't force garbage collection Function generate:Float[,](c:Int = 100, r:Int = 100) Local arr:Float[,] = New Float[c, r] Local temp:Float[,] = New Float[c, r] Print GCMemAlloced() Return arr EndFunction 'Force garbage collection Function GCgenerate:Float[,](c:Int = 100, r:Int = 100) Local arr:Float[,] = New Float[c, r] Local temp:Float[,] = New Float[c, r] temp = Null GCCollect Print GCMemAlloced() Return arr EndFunction The 'temp' array in the 'generate' function never has its memory reclaimed even though it was declared local to the function. However, when I force garbage collection as in the 'GCgenerate' function, everything is as it should be. Comment/Uncomment the appropriate lines and you should see the difference. Click the mouse button to display allocated memory to BlitzMax's 'output' tab. |
| ||
The temp array above is cleared, you just need to add a line that creates some garbage in your main loop to make the test a little more "real world":Local a$="garbageman" While Not KeyHit(KEY_ESCAPE) If MouseHit(1) Then Print GCMemAlloced() a:+"!" Wend |
| ||
Heh, the damn real world proves me wrong every time... |
| ||
this is slowly sinking in :) i am trying to make a 'next level' kind of function that clears everything...pseudo code: Type tNode Function createNode() 'create the node end function Method Delete() self.node.remove() 'this needs to be triggered... Print "Deleted tNode" End Method End Type Type tNextLevel Method New() For Local tmp:tNode Var = EachIn tNode.nodeList 'not sure how to use Var here... tmp = Null Next GCCollect() 'when all nodes = Null, collect and trigger destructor... ClearList(tNode.nodeList) End Method End Type Local a:tNode = tNode.createNode() Local tmp:tNextLevel = New tNextLevel i am new to blitz and OOP, i have a lot of syntax errors which is why this case makes a good practice for me :) i am curious about the Var in my eachinloop, i am trying to Null the reference tmp is pointing at, not tmp itself...when the reference is Nulled, the destructor is triggered, the 3D object is cleared and all is good...any pointers to where i am going wrong? i realize there are many ways to do things, and that clearing the node outside of the destructor and forget about clearing references might be better...but as mentioned - this is good practice for me :) |
| ||
When we use the NEW keyword to instantiate an object, that Object is created and put in memory. any variable that we assign to it is actually a reference to that object. This is an efficient way to deal with objects. Max uses a concept called reference counting to automatically allocate and release memory for objects. Basically, When an object is created, each reference to it is added to a special reference counter. When that counter reaches zero, the internal Garbage Collector will release the memory that was taken up by the Object.![]() Fortunately, The Garbage Collection System used by Max is automatic. This means that the garbage collector will do a sweep occasionally and remove any objects that do not have references associated to them. Our Job is to make sure we are managing the References (say Roger=null when we are done) and the Garbage Collector will take care of reference counting and freeing memory when the ref count for that object=0. |
| ||
I find the garbage collection to be fine but sometimes I manually clear stuff on certain screens when they are exited to free up system memory. You are more likley to get memory leaks from incorrect use of soundchannels. |
| ||
I don't think forcing GCCollect() is necessary. But I have found if I kill an object (type) that had a TList of objects in it, then those object do not die automagically. I had to add a .Clear() in the destructor (Delete() Method) of the Object for each TList it had that contained Objects. |
| ||
That clear memory leak was fixed some time ago with one of the 1.24 syncs. |
| ||
Are you responding to me? If so, I know for a fact that I need to clear my TLists. I just figured it out this morning. And I ran sync modules yesterday... I :ToString'd all the objects as I created them and then I :ToString'd all of them as they were destroyed. And many of them were not destroyed until I Clear()'d the TLists... |
| ||
I'm gonna go with TaskMaster on this one. It seems that if an Type contains a Field Tlist (And that is the only reference to it) when the Type is nulled, the List lives on. try this and then unrem the 'clearlist' line. Type Tmemtest Field FooList:TList = CreateList() Method New() For x = 1 To 10 ListAddLast Foolist , New Tfoo Next End Method End Type Type Tfoo Field big:Int[100] Method New() For x = 0 To Len(big)-1 big[x] = x Next End Method End Type Local test:TList = CreateList() Print "done creating" Graphics 640,480 Repeat Local mem:Int = GCMemAlloced() SetColor 0,0,255 DrawText "GCMemAlloced()="+mem, 0,0 DrawText " UP to add, DOWN to remove",0,20 DrawText CountList(test),0,40 SetColor 255,255,0 DrawRect (0,197,(mem*0.001)+6 ,46) SetColor 255,0,0 DrawRect (3,200,mem*0.001 ,40) If KeyDown(KEY_UP) ListAddLast test, New Tmemtest EndIf If KeyDown(KEY_DOWN) If Not test.isEmpty() Local a:Tmemtest = Tmemtest(test.last()) 'ClearList(a.FooList) ' <<<<<<<< <<<< < < < UNREM this line to test ListRemove test , a a = Null EndIf EndIf Flip Cls Until AppTerminate() Or KeyDown(KEY_ESCAPE) |
| ||
BTW, one thing I technically don't get is why the amount of garbage increases when hoovering a GUI canvas. (after a while it's free'd allright). |
| ||
Does anybody know if blitzMax GC removes unused circular references?Type A Field X:B End Type Type B Field Y:A End Type Global MyVar1:A = New A MyVar1.X = New B MyVar1.X.Y = MyVar1 'Danger! MyVar1 = Null This is an absolute bad desing example, just to ilustrate my doubt. In this example the variable MyVat is set to null but .. will it be cleared by the GC? |
| ||
Look in Advanced Topics in the Language help. There is a section on cyclic data structures. I don't understand them enough to answer your question directly though, sorry. |
| ||
ouch, this is a bit above my skills at the moment... excellent reference image bradford, it's a great help, thanks! i have discarded my memory leak paranoia, and i am counting on GC to do its job as long as references are out of scope or nulled...so far so good, thanks for all the help! |
| ||
Does the Release command affect any of these questions? |
| ||
Does the Release command affect any of these questions? No as Release only accepts an int variable and not an object. |
| ||
I'm gonna go with TaskMaster on this one. It seems that if an Type contains a Field Tlist (And that is the only reference to it) when the Type is nulled, the List lives on. Hmm, that's bad. If that's right, that needs to be fixed. |
| ||
so the moral is manually clear lists in your Type's destroy method. |
| ||
Hmmm...I must've had a big bowl of stooopid flakes for breakfast this morning because Bill's example seems to be showing that the memory is regained, whether ClearList() is used or not? The bar goes up...and the bar comes back down again...What am I supposed to be seeing? |
| ||
Same here and TaskManager shows no memory increase for the process or system as a whole. Running GC in debug mode shows objects being released as well. |
| ||
To answer the original question in the topic: it is not good practice to force GC every loop like I've seen in some code. However, it might be a good idea before a very memory-intensive block of code that is only executed once. Re, ziggy: No, it does not. The user guide warns about code like that. |
| ||
hehe, thanks otus - it took a few posts, but the answer arrived in the end :) a lot of confusing but interesting reading though, i understand how GCollect works a lot better now - thank you all for the help! |
| ||
It would be great to have a spetial sentence to remove circular dependencies. this could be a 'manual' gc call, and it doesn't matter if it is slow. It could be great to be able to call this sentence, as instance, when a level in your game is completed, before loading next level. I know this would force a sort of 'recursive' references deletion. but I think it is a good idea. Visual Studio GC does remove circular dependencies. |