force GCCollect, good practice?

BlitzMax Forums/BlitzMax Beginners Area/force GCCollect, good practice?

kimgar(Posted 2007) [#1]
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?


GfK(Posted 2007) [#2]
i've read several places not to rely on destructors, because GC works kind of random.
Places such as...?


kimgar(Posted 2007) [#3]

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?


GfK(Posted 2007) [#4]
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.


kimgar(Posted 2007) [#5]
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...


bradford6(Posted 2007) [#6]
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.


Who was John Galt?(Posted 2007) [#7]
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.


Gabriel(Posted 2007) [#8]
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.


impixi(Posted 2007) [#9]
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.


skidracer(Posted 2007) [#10]
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



impixi(Posted 2007) [#11]
Heh, the damn real world proves me wrong every time...


kimgar(Posted 2007) [#12]
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 :)


bradford6(Posted 2007) [#13]
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.


Grey Alien(Posted 2007) [#14]
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.


TaskMaster(Posted 2007) [#15]
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.


Dreamora(Posted 2007) [#16]
That clear memory leak was fixed some time ago with one of the 1.24 syncs.


TaskMaster(Posted 2007) [#17]
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...


bradford6(Posted 2007) [#18]
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)




CS_TBL(Posted 2007) [#19]
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).


ziggy(Posted 2007) [#20]
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?


Czar Flavius(Posted 2007) [#21]
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.


kimgar(Posted 2007) [#22]
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!


Czar Flavius(Posted 2007) [#23]
Does the Release command affect any of these questions?


tonyg(Posted 2007) [#24]
Does the Release command affect any of these questions?

No as Release only accepts an int variable and not an object.


Gabriel(Posted 2007) [#25]
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.


Grey Alien(Posted 2007) [#26]
so the moral is manually clear lists in your Type's destroy method.


Yan(Posted 2007) [#27]
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?


tonyg(Posted 2007) [#28]
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.


Otus(Posted 2007) [#29]
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.


kimgar(Posted 2007) [#30]
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!


ziggy(Posted 2007) [#31]
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.