Do Type pointers within Types get nulled?

BlitzMax Forums/BlitzMax Programming/Do Type pointers within Types get nulled?

Grey Alien(Posted 2007) [#1]
I could probably test this but someone may have a quick answer.

Say I have a type A, and one of it's fields (called P) points to another type B. Then I make an instance of type A and Type B, then I do A.P = B. Then later on I do B=null, I know that the GC won't collect it because A.P still exists. So my question is, if I do A=Null will Bmax automatically do A.P=null so that B can be GCed or do I have to "manually" free up any such type pointers. Thanks.

I've been working on the theory than you don't need to manually free them (unless they contain something that need special behaviour like Sound Channels or maybe lists (can't remember if MyList=null is safe or if you have to use MyList.Clear() so I always use Clear()).


tonyg(Posted 2007) [#2]
You could probably have tested it in less time than it took you to write the post.
Anyway yes, a.p will be nulled.
<edit> Now do you trust I am right or test that I'm right?
Type typea
	Field p:typeb
End Type
Type typeb
	Field x:Int=5
EndType
For Local loopy:Int = 0 To 2000
	a:typea = New typea
	b:typeb = New typeb
	a.p = b
	b = Null
	a = Null
	GCCollect()
	Print GCMemAlloced()
Next

(remove gccollect to see memory being obtained and released)
:-)


Grey Alien(Posted 2007) [#3]
Thanks. Hmm, If I comment out b=null and a=null then the memory doesn't go up because they are local variables. I'll make another test...


Grey Alien(Posted 2007) [#4]
here's a test. It never goes back to the start memory. Why not?
Strict

Type typea
	Field p:typeb
End Type

Type typeb
	Field x:Int=5
EndType

DisplayMem("start")

Local a:typea = New typea
DisplayMem("a created")
Local b:typeb = New typeb
DisplayMem("b created")
a.p = b
b = Null
DisplayMem("b nulled")
a = Null 
DisplayMem("a nulled")

Function DisplayMem(label$="")
	GCCollect()
	Print GCMemAlloced() + "  " + label
End Function


If you add in a.p = null it still doesn't got back down. Seems like A isn't out of scope or something.


Grey Alien(Posted 2007) [#5]
OK stuck the test in a function to ensure that A goes out of scope:

Strict

Type typea
	Field p:typeb
End Type

Type typeb
	Field x:Int=5
EndType

DisplayMem("start")
Test()
DisplayMem("end")

Function Test()
	Local a:typea = New typea
	DisplayMem("a created")
	Local b:typeb = New typeb
	DisplayMem("b created")
	a.p = b
	b = Null
	DisplayMem("b nulled")
	a = Null 
	DisplayMem("a nulled")
End Function

Function DisplayMem(label$="")
	GCCollect()
	Print GCMemAlloced() + "  " + label
End Function


This shows that a.p is automatically nulled.


tonyg(Posted 2007) [#6]
Aren't you creating string objects which are GC by doing gcmemalloced+" "+ label?
<edit> and would my local variables not go out of scope after the gccollect/gcmemalloced.
Never mind. You've answered your own questions.


Beaker(Posted 2007) [#7]
Be careful of circular references tho. ie.
a.p = b
b.p = a

They won't get garbage collected (or nulled), unless you go:
a.p = null
b.p = null



Grey Alien(Posted 2007) [#8]
yeah I have been very carefull to avoid those thanks. And where they are used (e.g. a list item pointing back to the list it's part of) I've made sure the list item has a Kill() method that nulls the pointer to the list so that when the list items are freed the main list variable is the only pointer left.


Grey Alien(Posted 2007) [#9]
Actually you only need to free up one of the pointers as the other one will get auto-nulled when the type is destroyed. See this code:

Strict

Type test
	Field p:test
End Type

DisplayMem("start")
testfunc()
DisplayMem("end")

Function testfunc()
	Local a:test = New test
	Local b:test = New test
	a.p = b
	b.p = a
'	a.p = Null 'uncomment this to see the circular ref fixed.
End Function

Function DisplayMem(label$="")
	GCCollect()
	Print GCMemAlloced() + "  " + label
End Function



Beaker(Posted 2007) [#10]
Of course. :)


Grey Alien(Posted 2007) [#11]
well I didn't know that until today and found out by accident. Thanks Beaker.


degac(Posted 2007) [#12]
Ok - I'm going little crazy now...
Strict
GCCollect()
Print GCMemAlloced()

Type mio
	Field valore:Int
	Field link:TLink
End Type

Local lista:tlist=New tlist
Local mi:mio = New mio

ListAddLast lista,mi
ClearList(lista)

mi = Null
lista=Null

GCCollect()
Print GCMemAlloced()

I run this code (with debug mod off) and I have the following results: 12060 12060 - good!

Then I make the SAME code with methods

Strict
GCCollect()
Print GCMemAlloced()

Type mio
	Field valore:Int
	Field link:TLink
End Type

Local lista:tlist=New tlist
Local mi:mio = New mio

lista.addlast mi
lista.clear

mi = Null
lista=Null

GCCollect()
Print GCMemAlloced()

I have the following results: 12060 - 12092
Why?
What's wrong with this source?


WendellM(Posted 2007) [#13]
Well, that is pretty weird, since .addlast is all that ListAddLast does and .clear is all that ClearList does (which you doubtless already know <g>). I get similar results.

The good news (I guess) is that it's a one-time effect. If you stick a For/Next loop around everything from "Local lista:tlist=New tlist" to "Print GCMemAlloced()" for 100 iterations in both programs, the memory doesn't go up each time. So it doesn't seem to be a "true" leak.

Check this, though. It's able to free up more memory than you started with by Null-ing the contents of the type instance before Null-ing the instance itself (even though they're already Null since they've never been assigned values):
Strict
GCCollect()
Print GCMemAlloced()

Type mio
	Field valore:Int
	Field link:TLink
End Type

Local lista:TList=New TList
Local mi:mio = New mio

ListAddLast lista,mi
ClearList(lista)

mi.valore = Null
mi.link = Null
mi = Null
lista=Null

GCCollect()
Print GCMemAlloced()

I get:

13960
13956

All this is weird. I could be wrong (easily!) but I'm thinking that it might just be a glitch with byte boundaries or something and isn't significant. It'd be nice to know what's really going on just for curiosity's sake, but it doesn't seem to be a real problem since it's a one-time deal that doesn't change when done multiple times.


degac(Posted 2007) [#14]
No, it's not a real problem (the difference is really small), but it is the fact that methods are called by ListClear() and the results should be the same (I think!)!!!
Well the good new is I'm not going crazy...
Thanks!


Grey Alien(Posted 2007) [#15]
It's not a valid test. You must run the test in a function otherwise the Local variables will NOT be freed up, even when you call GCCollect(). I found this out when making my own tests early on:

Strict
GCCollect()
Print GCMemAlloced()

Type mio
	Field valore:Int
	Field link:TLink
End Type

f()

GCCollect()
Print GCMemAlloced()

Function f()
	Local lista:TList=New TList
	Local mi:mio = New mio
	
	lista.addlast mi
	lista.clear
	
	mi = Null
	lista=Null
End Function


gives:

14948
14948


degac(Posted 2007) [#16]
Oh! Thanks Grey! Now it's more clear!
So having Global in the 'main' source (like in my example) doesn't guarantee the memory allocated is then freed up correctly?
I need to make some test...
Thanks anyway


Grey Alien(Posted 2007) [#17]
yeah basically globla or local in the main source doesn't guarantee it being cleared, whereas it will be cleared if in a function.