Memory leak :/

BlitzMax Forums/BlitzMax Beginners Area/Memory leak :/

nawi(Posted 2005) [#1]
My first topic here so hello to anybody I know from irc/blitzcoder.com, but back to the topic:

I just got Blitzmax and all that and Ive started with simple particle thing. The thing is that Ive got memory leak :/.
Check the shortly code: (Doesnt require media)

Strict

Const ScreenWidth = 640,ScreenHeight = 480,ScreenDepth = 16,ScreenHerz = 60

Const Gravity# = 0.1

Const ParticleCount = 200

Global Particles

Global ParticleList:TList = New TList

'FPS
Global FPS,ShowFPS,FPSTimer

Type Particle
	Field X#,Y#
	Field XAcc#,YAcc#
	Field Life
	
	Method Update()
		YAcc# = YAcc# + Gravity#
		X# = X# + XAcc#
		Y# = Y# + YAcc#
		Life = Life - 1
		Plot X#,Y#
	End Method
	
	Function CreateParticle(PX#,PY#,PLife)
		Local P:Particle = New Particle
		P.X# = PX#
		P.Y# = PY#
		P.Life = PLife
		
		P.XAcc# = -1 + Rnd(2) 
		P.YAcc# = 1+Rnd(1)
		ParticleList.AddLast P
		
		Particles :+1
	
	End Function
End Type


Load
Main

Function Load()
	Graphics ScreenWidth,ScreenHeight,ScreenDepth,ScreenHerz
End Function 

Function Main()
	Local Loop

	Repeat
		For Loop = 1 To ParticleCount
			Particle.CreateParticle(Rand(ScreenWidth),0,90)
		Next
		UpdateParticles()
		UpdateFPS()
		DrawText "Particles: " + Particles,0,15
		DrawText "Memory: " + MemAlloced(),0,30
		Flip
		Cls
		FlushMem
	Until KeyHit( KEY_ESCAPE )
End Function

Function UpdateParticles()
	SetColor(0,0,255)
	For Local P:Particle = EachIn ParticleList
		P.Update()
		If P.Life = 0 Then
			ListRemove(ParticleList,P:Particle)
			Particles :-1
		EndIf
	Next
End Function

Function UpdateFPS()
	FPS = FPS + 1
	If MilliSecs() > FPSTimer + 999 Then
		ShowFPS = FPS
		FPS = 0
		FPSTimer = MilliSecs()
	EndIf
	SetColor(255,255,255)
	DrawText "FPS: " + ShowFPS,0,0
End Function


Memory just keeps running :/


Dreamora(Posted 2005) [#2]
Bug mentioned quite some time ago in the bug board.
Never use flushmem outside the mainloop! ( especially not in a loop )

calling flushmem once per loop as flip is all that is needed ( garbage collectors need time to process their stuff. the more often you call it the slower your app will be in the end just because of that )


nawi(Posted 2005) [#3]
Fixed now. The problem was with Release. Whats that for then :O


Dreamora(Posted 2005) [#4]
yes you create every loop ParticleCount new particles and I don't think that all of them disappear until the next flip ( otherwise they only blink for a single frame )


FlameDuck(Posted 2005) [#5]
The problem was with Release. Whats that for then :O

Release is for telling the garbage collector that you're done with an integer reference (as opposed to an object reference, which BlitzMAX keeps track of automagically), and thus informing BlitzMAX it is safe for the garbage collector to release an object, if its reference count = 0.

Never, ever use Release in conjunction with real objects (that is one that is declared like p:Particle), as it should only ever be used with ints. Release is only there for the handful of people reluctant to embrace OOP.


nawi(Posted 2005) [#6]
Thanks I think I understand it now.


AntonyWells(Posted 2005) [#7]
Never use flushmem outside the mainloop!


Doesn't that contridict what Mark previous said about flushmem being responsible for cleaning up the scope of it's call only?
If I only use flushmem in my main loop, what happens to my custom modules when they try to free memory?

*confused*


Warren(Posted 2005) [#8]
We're all confused over FlushMem. Nobody seems to have any idea how it works since it doesn't work in the one way that would seem logical...


Beaker(Posted 2005) [#9]
Ignore Dreamora and all is well. :)


Floyd(Posted 2005) [#10]
Well, it works exactly as advertised in the example at the top of this thread. So it can't be that complicated.

Resist the temptation to scatter calls to FlushMem throughout your code. Just call FlushMem once in your main loop.

Release is in the language as a kind of hack to allow old style Blitz code: OrdinaryInt = LoadImage("something").

This style is not using the object system and can't do automatic garbage collection. So there has to be a way to manually release things.

The Release command currently allows you to Release an object. That is a mistake and will be fixed.


FlameDuck(Posted 2005) [#11]
Doesn't that contridict what Mark previous said about flushmem being responsible for cleaning up the scope of it's call only?
It's wonderful how selective quoting will add to all the confusion. That's not what he said at all. Flushmen clears up all memory from the top of whatever scope it's called, and recursively for anything that branches off it (which is logically a subset of the 'parent' scope anyway). [a /posts.php?topic=41179]Clicky[/b].

Nobody seems to have any idea how it works since it doesn't work in the one way that would seem logical...
It doesn't? What is the one way that seems logical then?


skn3(Posted 2005) [#12]
Well I for one have experienced the very strange behaviour of flushmem(). Putting it in a function, even if that function returns before it should call seems to call it anyway.

http://www.blitzbasic.com/Community/posts.php?topic=42767

If you remove it you get even more problems when you try and call flushmem from an external block. IE the main loop.


Warren(Posted 2005) [#13]
It doesn't? What is the one way that seems logical then?

And I quote the documentation:

"FlushMem : Discard all unused memory"

No caveats, no restrictions, no weird usage rules ... discard all unused memory. Why in the world would you only want to discard unused memory from certain parts of your program but not from others? Pointless "feature" that only causes confusion.


AntonyWells(Posted 2005) [#14]

It's wonderful how selective quoting will add to all the confusion. That's not what he said at all.



Selective quoting? The technical term is memory. People assemilate information, and said information becomes less coherent over the course of time.

Or in your native tongue, http://www.google.com/search?q=memory&sourceid=opera&num=0&ie=utf-8&oe=utf-8


FlameDuck(Posted 2005) [#15]
No caveats, no restrictions, no weird usage rules ... discard all unused memory.
And it does. The restriction (that it must be called from the 'root' scope of a program) is not one imposed by design (at least not intentionally), but is probably a side-effect of not having a 'proper' garbage collector, but instead relying on reference counts.

Why in the world would you only want to discard unused memory from certain parts of your program but not from others?
I don't know. I'm assuming for the same reason people will want to do other similar chores (Flip, Cls etc.)?

The only problem I've encountered with it is that it doesn't handle cyclic references very elegantly (or rather not at all).

Selective quoting? The technical term is memory. People assemilate information, and said information becomes less coherent over the course of time.
Then perhaps people with piss poor memories shouldn't post their incoherent information as fact?

Feel free to stop trolling and throwing about thinly veiled insults around. If you have a problem with me Antony, just say it.


Warren(Posted 2005) [#16]
And it does. The restriction (that it must be called from the 'root' scope of a program) is not one imposed by design (at least not intentionally), but is probably a side-effect of not having a 'proper' garbage collector, but instead relying on reference counts.

You tell me it does what I want, and then immediately start telling me about a restriction. I said:

No caveats, no restrictions, no weird usage rules ... discard all unused memory.


So long as we agree it's broken, we can move on.


AntonyWells(Posted 2005) [#17]

Then perhaps people with piss poor memories shouldn't post their incoherent information as fact?

Feel free to stop trolling and throwing about thinly veiled insults around. If you have a problem with me Antony, just say it.


I did not have a problem with you, I had a problem with you assuming I selectively quoted mark to make my point and,
Besides that, if you didn't get the joke (Google) that doesn't automagically transform it into an insult.


Then perhaps people with piss poor memories shouldn't post their incoherent information as fact?




Perhaps you should rephrase that to represent the facts, as at the moment it is something of an ironic statement, considering I was stating nothing as fact, merely asking dreamora a question. Asked, not stated.

*Confusion* at the end just to ram home the point I wasn't sure.

At what point was I stating fact? Or did your memory just fail you?


Amon_old(Posted 2005) [#18]
Can I just intervene her and ask when the correct time to use flushmem is?

I have my main loop with flushmem after flip. Now in my main loop there is a function which has its own loop in it. Kind of like having function calls which are for subgames.

Should I add flushmem to the end of that loop also or leave it only in the main loop?


skidracer(Posted 2005) [#19]
Only if that loop is creating a lot of garbage each iteration, if for instance it was doing a 100+ string operations it would be beneficial for the innerloop to be calling FlushMem so those strings can be recycled.


FlameDuck(Posted 2005) [#20]
You tell me it does what I want, and then immediately start telling me about a restriction.
No I didn't. I don't even know what you want. I told you it worked in a way that seemed perfectly logical. I'm sorry you feel it's broken.


Warren(Posted 2005) [#21]
No I didn't. I don't even know what you want.

Scroll up.

I told you it worked in a way that seemed perfectly logical.

And if the documentation said it worked that way, I would accept it and move on. But since it doesn't, I'd like either the documentation or the command to be changed so they match up. That way the documentation could begin to approach the "useful" watermark...

I'm sorry you feel it's broken.

And I'm sorry you have no idea what is being discussed (despite the fact that it was laid out in plain English), yet still felt the need to fill the thread with your usual barrage of noise.

In the future, if you can't contribute - please don't reply. It just makes the thread noisy and lessens the chance of BRL actually hearing the stuff we're asking about.


teamonkey(Posted 2005) [#22]
Why in the world would you only want to discard unused memory from certain parts of your program but not from others?

Because it's reduces memory fragmentation. For example, if you have a small function that creates and discards a lot of memory, you can happily FlushMem at the end of the function knowing that it will release only the memory created in that scope and that providing there are no references left over, the whole block of memory can be re-used immediately. If FlushMem cleaned out all unused memory in one go, memory would become highly fragmented as some objects from other scopes would be released and others wouldn't.


AntonyWells(Posted 2005) [#23]

If FlushMem cleaned out all unused memory in one go, memory would become highly fragmented as some objects from other scopes would be released and others wouldn't.



That problem exists anyway. My ase loader baloons up to 900mb of v-mem before eventually crashing.

Because I don't know how to code? Because ase is an incredibly tricky format? or because flushmem/gc don't work in a sensible well thought out way?

Clue:It's not one of the first two. :)


teamonkey(Posted 2005) [#24]
Well it's certainly not the last one. Post your code if you've got a memory leak.


AntonyWells(Posted 2005) [#25]
Then there must be a secret 4th option..

Here's the code, probably of zero use since it's for trinity, but anyway.

Last version i tried plenty of things, flushmeming once per loop, once per every hundred loops,
and all that is doing is loading the ase(Which it does instantly almost) then reading it from a bank stream per line.
I did a ase loader in b3d once for vivid, simple but it seemed to run alot better.(If you load a 1mb file with the below, it'll hang forever. very small files worked perfectly.)(-edit-debug stuff is still in there, it runs fine until you remove the contiue)



FlameDuck(Posted 2005) [#26]
If FlushMem cleaned out all unused memory in one go, memory would become highly fragmented as some objects from other scopes would be released and others wouldn't.
No it wouldn't. You have no (zero!) control over how the operating system pages memory.


skidracer(Posted 2005) [#27]
Yes it would. BlitzMax uses it's own memory pools for optimized memory recycling.

Ant: I think the problem could be your FlushMem on line 133 is inside an If block. Try replacing that code with

If memf<500 continue
FlushMem
Wend


FlameDuck(Posted 2005) [#28]
Yes it would. BlitzMax uses it's own memory pools for optimized memory recycling.
Now why would you want to go and do that?


AntonyWells(Posted 2005) [#29]
Because when you request/release mem memory unamanged it fragments. Only by reserveing a larger pool and resizing that in big(not as needed) chunks can you avoid that. Natch. (Guessware.)

Skid, thanks for the tip but the above was pretty much the last thing i tried. It's not THAT common a problem, but there does seem to be a few extreme cases that max doesn't like, specifically memory/string related.(or perhaps antony/fool related ;))


FlameDuck(Posted 2005) [#30]
Because when you request/release mem memory unamanged it fragments.
Yeah. On the Amiga (and maybe Windows95). Not on a modern OS. That's why operating systems like Linux and WindowsNT can run indefinately. They all have mechanisms for making sure memory stays unfragmented. Linux for example uses the buddy-heap principle for managing memory fragmentation.

Only by reserveing a larger pool and resizing that in big(not as needed) chunks can you avoid that.
So you're saying that BlitzMAX programs require some mystery ammount of RAM to run? And even though you might actually have this mystery ammount the program will still fail, if you don't have it in contiguous (aka. unfragmented) memory?

I'm sure that's a requirement they forgot to "put on the box". I for one would like to know what the minimum size of this unfragmented, unpaged memmory is, and how often one is likely to encounter it on typical low-specification machines.

Oh, and the other way to "avoid it" is to upgrade to an OS from this century, with built-in (and might I add, highly optimised [since it interfaces with the MMU directly, rather than arbitrarilly]) memory management functions.