Freeing up memory, expected behavior?

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
User avatar
Smoggert
Prole
Posts: 29
Joined: Thu Nov 10, 2016 11:23 pm

Freeing up memory, expected behavior?

Post by Smoggert »

So,

I have a little program that generates a Simplex noise based isometric voxel-like 2.5D terrain (with varriable width and height).
The program is clearly far from perfect but it's not the main cause of concern.

In my main file, I load a new terrainobject and allow scaling/moving through mousewheel and arrow keys.
When you hit F5. Load gets called but with adjusted scaling/ x/ y so the new terrain object (random seed) is in the same position and scaled like the old one.

So far so good. Now the issue: if I don't set my game.terrain to nil before creating a new terrain, garbage collection never seems to notice that the old one has no more references. My memory usage goes up by 500mb/refresh and the game crashes at 2GB.

Is this expected behavior? Am I always supposed to execute game.terrain = nil before loading in a new object? Or is this faulty GC?

Code: Select all

function love.load()
	--- 	INIT VALUES
	game.t = 0
	game.fps = 0
	game.mX, game.mY = 0, 0
	game.mapX, game.mapY = 0, 0
	---// 	INIT VALUES
	--- 	CONSTANTS
	game.width, game.height, game.size= 501,501,20
	---//
	
	game.dx, game.dy = Terrain.getCenter(game.width, game.height, game.size)
	game.x, game.y = game.x or love.graphics.getWidth()/2 - game.dx, game.y or love.graphics.getHeight()/2 - game.dy
	
	--game.terrain = nil  <---------------------------
	game.terrain = Terrain.new(game.x, game.y ,game.width, game.height, game.size)
	game.terrain.scale = game.scale or game.terrain.scale
end
Attachments
SimplexTerrain.love
(5.1 KiB) Downloaded 66 times
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: Freeing up memory, expected behavior?

Post by raidho36 »

Probably convoluted memory islands, i.e. groups of objects that reference each other. GC is not a silver bullet, it can only handle simple enough cases.
User avatar
slime
Solid Snayke
Posts: 3143
Joined: Mon Aug 23, 2010 6:45 am
Location: Nova Scotia, Canada
Contact:

Re: Freeing up memory, expected behavior?

Post by slime »

raidho36 wrote:Probably convoluted memory islands, i.e. groups of objects that reference each other. GC is not a silver bullet, it can only handle simple enough cases.
Lua's GC does handle those cases.
Fuzzlix
Citizen
Posts: 60
Joined: Thu Oct 13, 2016 5:36 pm

Re: Freeing up memory, expected behavior?

Post by Fuzzlix »

love.load() is only called one time at love start and you create one instance of Terrain. I cant find a second place where you release this Instance.
The bug is probably you call love load() manually in your love.keypressed. Dont do that! :)
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: Freeing up memory, expected behavior?

Post by raidho36 »

slime wrote:Lua's GC does handle those cases.
Apparently not all of them, otherwise this would not be happening.
Fuzzlix wrote: The bug is probably you call love load() manually in your love.keypressed. Dont do that! :)
This is not a special function, it's just the framework will call it for you when it's ready to operate. You can call it from anywhere manually and that does not impacts anything.
User avatar
Smoggert
Prole
Posts: 29
Joined: Thu Nov 10, 2016 11:23 pm

Re: Freeing up memory, expected behavior?

Post by Smoggert »

I just tested some more, and without nilling my terrain pointer, I can stop the game from going haywire by running collectgarbage("collect") manually at the end of load()

I kinda wonder now if nilling a pointer automatically calls collectgarbage("collect") on that pointer's data or something.

So I guess it is indeed a memory island kinda deal, given I run everything in locals I don't see how its created. But \o/ a well.

Good thing to know for the future when or if I'd ever run into real memory issues.

Code: Select all

function love.load()
	--- 	INIT VALUES
	game.t = 0
	game.fps = 0
	game.mX, game.mY = 0, 0
	game.mapX, game.mapY = 0, 0
	game.memusage = 0
	---// 	INIT VALUES
	--- 	CONSTANTS
	game.width, game.height, game.size= 501,501,20
	---//
	
	game.dx, game.dy = Terrain.getCenter(game.width, game.height, game.size)
	game.x, game.y = game.x or love.graphics.getWidth()/2 - game.dx, game.y or love.graphics.getHeight()/2 - game.dy
	
	--game.terrain = nil
	game.terrain = Terrain.new(game.x, game.y ,game.width, game.height, game.size)
	game.terrain.scale = game.scale or game.terrain.scale
	collectgarbage("collect")
end

function love.keypressed(key, scancode, isRepeat)
	game.terrain:keypressed(key)
	if key == "escape" then		love.event.quit()	end
	if key == "f5" then	 game.x = game.terrain.x; game.y = game.terrain.y; game.scale = game.terrain.scale;	love.load() end
end
User avatar
pgimeno
Party member
Posts: 3588
Joined: Sun Oct 18, 2015 2:58 pm

Re: Freeing up memory, expected behavior?

Post by pgimeno »

I've run into similar issues when converting a canvas to an ImageData. My understanding is that the garbage collector barely has time to run when the CPU load is high. I didn't find a solution other than running collectgarbage.
https://github.com/pgimeno/Thrust-II-re ... e.lua#L512

One hypothesis that comes to mind is that clearing the variable first dereferences the object, while replacing it with another object doesn't, and instead leaves that up to the garbage collector.
User avatar
slime
Solid Snayke
Posts: 3143
Joined: Mon Aug 23, 2010 6:45 am
Location: Nova Scotia, Canada
Contact:

Re: Freeing up memory, expected behavior?

Post by slime »

Nothing will free the memory immediately - you just have one more operation that can cause a garbage collection cycle, if you nil the variable first. In either case it will still take time before it's collected unless you call collectgarbage() yourself.
User avatar
ivan
Party member
Posts: 1911
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Freeing up memory, expected behavior?

Post by ivan »

slime wrote:Nothing will free the memory immediately - you just have one more operation that can cause a garbage collection cycle, if you nil the variable first. In either case it will still take time before it's collected unless you call collectgarbage() yourself.
Yes, slime is correct. collectgarbage('collect') is the only way to release any GC-ed memory and it will work with cycles too.
It should be pointed out that love2d does its own thing with the garbage collector so it's not exactly the same as with standard Lua.

Smoggert, you are storing your "blocks" in a 1-dimensional table which is a good start,
but since each "block" is a table, that's 500x500x30 tables.
Looks like the only unique data for each block is the "noise" value,
if you need more data, you can use parallel tables to store the map:

Code: Select all

map =
{
  noise_table[index1d] = number
  scale_table[index1d] = number
}
This should reduce the memory consumption quite a lot.

Regarding the block class: you are caching too much redundant information, for example:
"x, y, z" can be deduced from the block's index/position in the map.
and surfaces/normals can be computed on the fly too.
User avatar
Smoggert
Prole
Posts: 29
Joined: Thu Nov 10, 2016 11:23 pm

Re: Freeing up memory, expected behavior?

Post by Smoggert »

ivan wrote: Regarding the block class: you are caching too much redundant information, for example:
"x, y, z" can be deduced from the block's index/position in the map.
and surfaces/normals can be computed on the fly too.
Yeh fair points, the classes were just hastely put together with not much thought behind them.
I'll try implementing your example later this week. See how much of a difference a few 100000 less tables makes :D
I'm well aware I'm using way more memory than needed. But thanks for the pointers either away !
Post Reply

Who is online

Users browsing this forum: Bing [Bot], NikitaPupy and 9 guests