Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GC] GCSetMode() only a stub, Garbage Collection on Windows vs Linux #251

Open
GWRon opened this issue Jan 3, 2023 · 3 comments
Open

[GC] GCSetMode() only a stub, Garbage Collection on Windows vs Linux #251

GWRon opened this issue Jan 3, 2023 · 3 comments

Comments

@GWRon
Copy link
Contributor

GWRon commented Jan 3, 2023

For now GCSuspend() does not just suspend garbage "collecting", it actually stops the GC overall (it calls GC_Disable() from bdwgc).
GCSetMode() is not having an implementation -> it just does nothing for now (but is still in the documentation)

Now the interesting thing:
When disabling the GC in my game (TVTower) the memory usage increases on Linux and also on Windows. I eg wait for the memory usage (also the one reporty by GCMemAlloced()) to raise from eg 150MB to 500 MB (fast forward helps creating a lot of temporary stuff).

Now I enable the GC again:
On Linux it takes some seconds to lower memory usage in multiple steps again to about the "original" value. (so from 500 down to around 170 MB)
On Windows it simply keeps at the memory usage where it was before (stays at 500 and slowly increaes with each newly added managed data).

Does this mean it actually collects the objects created after GCSuspend() ? Or does it clean up differently on Linux than on Windows. What I mean is: some object might reference the "non managed" data and once this managed object got out of scope it cleaned the non-managed ones too. On windows the removal might happen in a different kind of "order" and thus making the GC not see the non-managed objects.

I tried to replicate the issue in a separate file ... but it shows consistent behaviour across Windows and Linux:
(In this sample it is not able to also delete the 100mb of "unmanaged" data)

SuperStrict
Framework Brl.StandardIO
Import Brl.Bank


Type TMemBlock
	Field id:int
	Field block:TBank

	Global lastID:int
	
	Method New()
		lastID :+ 1
		id = lastID
		block = CreateBank(10 * 1024 * 1024) '10 MByte
	End Method
	
	
	Method Delete()
		print "Memblock " + id +" deleted."
	End Method
End Type

print "GCMemAlloced() = " + GCMemAlloced()

'add managed blocks
Local blocks1:TMemBlock[10]
For local i:int = 0 until blocks1.length
	blocks1[i] = New TMemBlock()
Next
print "added managed blocks: GCMemAlloced() = " + GCMemAlloced()


GCSuspend()
'add unmanaged(?) blocks
Local blocks2:TMemBlock[10]
For local i:int = 0 until blocks2.length
	blocks2[i] = New TMemBlock()
Next
print "added unmanaged blocks: GCMemAlloced() = " + GCMemAlloced()

GCResume()
GCCollect()
print "GCMemAlloced() = " + GCMemAlloced()

blocks1 = New TMemBlock[0]
blocks2 = New TMemBlock[0]
GCCollect()
print "Freed blocks. GCMemAlloced() = " + GCMemAlloced()
Delay(2500)
GCCollect()
print "Waited 2500ms. GCMemAlloced() = " + GCMemAlloced()
Delay(2500)
GCCollect()
print "Waited 2500ms. GCMemAlloced() = " + GCMemAlloced()
Delay(2500)
GCCollect()
print "Waited 2500ms. GCMemAlloced() = " + GCMemAlloced() 'for me memory goes down here
Delay(2500)
GCCollect()
print "Waited 2500ms. GCMemAlloced() = " + GCMemAlloced()
Delay(2500)
GCCollect()
print "Waited 2500ms. GCMemAlloced() = " + GCMemAlloced()

Output:

Executing:tetst.gc
GCMemAlloced() = 262144
added managed blocks: GCMemAlloced() = 104906752
added unmanaged blocks: GCMemAlloced() = 209772544
GCMemAlloced() = 209772544
Memblock 7 deleted.
Memblock 10 deleted.
Memblock 9 deleted.
Memblock 1 deleted.
Memblock 3 deleted.
Memblock 4 deleted.
Memblock 2 deleted.
Memblock 6 deleted.
Memblock 5 deleted.
Memblock 8 deleted.
Freed blocks. GCMemAlloced() = 209772544
Waited 2500ms. GCMemAlloced() = 209772544
Waited 2500ms. GCMemAlloced() = 209772544
Waited 2500ms. GCMemAlloced() = 104914944
Waited 2500ms. GCMemAlloced() = 104914944
Waited 2500ms. GCMemAlloced() = 104914944

-> only first 10 blocks' Delete() is called as expected
-> final GCMemAlloced() states, there is still 100 MB of memory allocated

This means GCMemAlloced() reported to still manage (!) around 100 MB of data

`Function GCMemAlloced:Size_T()'
Memory allocated by application

This function only returns 'managed memory'. This includes all objects, strings and arrays in use by the application.

If the GC sees the 100 MB as managed why does it never free it ?

If you moved blocks1 = New TMemBlock[0] to happen before GCResume() the GC still frees them properly ( and calls "Delete()" on them, once GCResume() was executed)

@GWRon
Copy link
Contributor Author

GWRon commented Jan 3, 2023

I extended the sample so it creates a third block of 100MB ... and now it also frees the memory of the blocks (blocks2) created after GCSuspend() (but keeps "blocks3" in memory :D).

This means objects created after "GCSuspend()" seem still properly managed.

This makes me wonder now - why the memory usage in my game stays "high" on Windows but can get lowered properly on Linux.
Must be data which is handled differently between Windows and Linux.

@GWRon GWRon changed the title [GC] GCSetMode() only a stub, No way to pause garbage collection [GC] GCSetMode() only a stub, Garbage Collection on Windows vs Linux Jan 3, 2023
@GWRon
Copy link
Contributor Author

GWRon commented Jan 3, 2023

In my game : download current code from github.com/TVTower/TVTower
Open main.bmx, find

If KeyManager.IsHit(KEY_Y)

Append:

                global gcOn:int = True
                if gcOn
                    GCSuspend()
                    print "GCSuspend()"
                    gcOn = False
                else
                    GCResume()
                    print "GCResume()"
                    gcOn = True
                endif

Compile TVTower.bmx (and execute)
Start a new Game
press "y" to disable or enable the GC
press "cursor right" to move fast forward (should increase memory if GC is off)
press "y" again to enable the GC ... but memory usage stays high (on Windows) or gets lower (on Linux)
Feel free to append your own "print GCMemAlloced()"

@GWRon
Copy link
Contributor Author

GWRon commented Jan 3, 2023

Dunno if it would be useful to have additional GC information at hand: fill_prof_stats() or the wrapper GC_get_prof_stats()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant