Hey, y'all, first post here. I'm making a 3D game, so I rely on LÖVE's built in meshes, and I use them heavily, creating many of them at a time when needed and freeing them when needed no longer, because it'd otherwise use a lot of memory to keep them around. However, I seem to have memory issues regardless, and I feel like it's an issue with LÖVE itself, not my own code.
Here's the issue I'm facing: I create a large amount of mesh objects all at once, and then later on I'll nil a bunch of them at the same time and have Lua garbage collect them, and Lua's own collectgarbage("count") value decreases back to the value from before they were created, but the memory count in Task Manager only decreases slightly and never comes close to the original memory value. Is LÖVE failing to free the mesh objects from memory after they're garbage collected?
This is a big issue, because the game loads and unloads tons of mesh data, and after a while memory usage will hang around the 500-600 MB range, when it's typically only between 250-350 MB. I've debugged everything madly and can't seem to find a leak in my own code, so I made a small .love file where I managed to reproduce the issue.
I've attached the .love file to this post. To reproduce the issue, launch the program and Task Manager. The automatic garbage collector will be stopped. Hold F to create meshes without storing them anywhere until you've used up a ludicrous amount of memory, then press G to run a full garbage collection cycle. Lua will report the meshes as having been garbage collected, but Task Manager will not. Here's a visual of what I mean.
I'm running 0.10.2, and I've tested this on Windows with Task Manager, macOS with its own Activity Monitor application, and I even compiled the latest version of 0.11.0 from the repo and tested that, and I get the same results each time.
Is there some way to fix this?
Mesh Garbage Collection Issue
Forum rules
Before you make a thread asking for help, read this.
Before you make a thread asking for help, read this.
Mesh Garbage Collection Issue
- Attachments
-
- meshgarbage.love
- (798 Bytes) Downloaded 87 times
Last edited by Bird on Thu Jan 04, 2018 3:21 pm, edited 1 time in total.
Re: Mesh Garbage Collection Issue
Allocating and freeing ram takes quite a while and fragments your data across the ram.
When we wrote a game engine at University we would allocate 500MB initially and then 100MB chunks as needed because you will need the ram anyway.
I'm not entirely sure how love handles this. But if it's not constantly increasing it as you load and unload them I wouldn't give this much thought.
If there ain't no leak then there ain't no problem!
When we wrote a game engine at University we would allocate 500MB initially and then 100MB chunks as needed because you will need the ram anyway.
I'm not entirely sure how love handles this. But if it's not constantly increasing it as you load and unload them I wouldn't give this much thought.
If there ain't no leak then there ain't no problem!
Re: Mesh Garbage Collection Issue
I tried running the same test but with many 500x500 ImageData objects, which are similarly managed internally by LÖVE, instead of 3000 vert meshes, and as soon as the garbage is collected by Lua, LÖVE's memory usage consistently goes back down. In a test, I got the ImageData garbage up to 1000 MB memory used, and it went down to ~50 MB after garbage collection. I'd make the assuption that LÖVE's memory usage reflects what's actively being held in memory.
With meshes, it depends on how many are created, but I ended up going from ~1000 MB to ~700 MB in my test after full garbage collection.
I feel like something weird is happening here.
Re: Mesh Garbage Collection Issue
Have you tried allocating, deleting and allocating again?
Or allocating meshes, deleting them and allocating image data for that matter?
It does sound like a bug but before we go all up in arms we might wanna verify that it's an issue and not just an inconsistency
I'll test as soon as I'm home and see if I can dig something up.
Edit: In my tests both imagedata and meshes behave pretty much the same.
I prepared a mesh generation function and an image data generation function.
Mesh was generating 1k meshes with 10k vertecies. Bumped me up to ~230MB.
Imagedata was generating 50 with a size of 1000x1000 pixels. Bumped me up to ~225MB.
Both seem to allocate slightly more data over time. With ~100KB increased total ram usage every time gc runs.
Your results seem more like you are keeping references somewhere.
This small leak does look weird though.
Or allocating meshes, deleting them and allocating image data for that matter?
It does sound like a bug but before we go all up in arms we might wanna verify that it's an issue and not just an inconsistency
I'll test as soon as I'm home and see if I can dig something up.
Edit: In my tests both imagedata and meshes behave pretty much the same.
I prepared a mesh generation function and an image data generation function.
Mesh was generating 1k meshes with 10k vertecies. Bumped me up to ~230MB.
Imagedata was generating 50 with a size of 1000x1000 pixels. Bumped me up to ~225MB.
Both seem to allocate slightly more data over time. With ~100KB increased total ram usage every time gc runs.
Code: Select all
function love.load()
vertecies = {}
for i=1,10000 do
table.insert(vertecies, {i, i})
end
end
function love.keypressed(key)
if key == "q" then
for i=1,1000 do
love.graphics.newMesh( vertecies )
end
elseif key == "e" then
for i=1,50 do
love.image.newImageData( 1000, 1000 )
end
elseif key == "w" then
collectgarbage()
end
end
This small leak does look weird though.
Re: Mesh Garbage Collection Issue
Both of our codes essentially do the same thing, but yours ends up with a ram value that's not too high, compared to mine which fails to free at least 50% of the ram that was present before garbage collection. I double checked, and I'm not storing the mesh anywhere, besides a local variable that goes unused. Removing the local variable assignment causes no significant change from the previous result.
However, I noticed one big difference, and it's that your vertices table is generated once and reused, and mine are created and used individually for each mesh.
I tried adjusting your code to create a new vertices table for each mesh (local so it's cleaned up properly), but I found that the garbage collector was doing its job of keeping ram usage low, so I didn't see any ram changes. I added a collectgarbage("stop") before generating the vertices and meshes and a "restart" when collecting garbage, and it seems to leave a LOT more unfreed ram.
With one "q" press, it reaches 1.13 GB of ram, and collecting garbage causes it to decrease to the 400-500 MB range. It's the same issue I'm running into, and it seems to be caused by the vertices table used with the mesh, possibly. Does using a vertices table with a mesh stop the table from being garbage collected even after the mesh itself is freed?
I ran another test later where I stored both the mesh objects and vertex tables in a weak table and drew the count on screen (using pairs). It would increase accordingly, and it printed 0 when garbage was collected, as expected. It doesn't seem to be an issue with the code nor Lua itself, because everything is being collected properly.
Maybe LÖVE isn't cleaning up all the mesh data when mesh objects are destroyed, or it forgets to free the vertex data in some cases? I can't tell what exactly is going on, so I'm just guessing at this point.
However, I noticed one big difference, and it's that your vertices table is generated once and reused, and mine are created and used individually for each mesh.
I tried adjusting your code to create a new vertices table for each mesh (local so it's cleaned up properly), but I found that the garbage collector was doing its job of keeping ram usage low, so I didn't see any ram changes. I added a collectgarbage("stop") before generating the vertices and meshes and a "restart" when collecting garbage, and it seems to leave a LOT more unfreed ram.
Code: Select all
function love.keypressed(key)
if key == "q" then
collectgarbage("stop")
for i=1,1000 do
local vertecies = {}
for i=1,10000 do
table.insert(vertecies, {i, i})
end
love.graphics.newMesh( vertecies )
end
elseif key == "e" then
for i=1,50 do
love.image.newImageData( 1000, 1000 )
end
elseif key == "w" then
collectgarbage()
collectgarbage("restart")
end
end
I ran another test later where I stored both the mesh objects and vertex tables in a weak table and drew the count on screen (using pairs). It would increase accordingly, and it printed 0 when garbage was collected, as expected. It doesn't seem to be an issue with the code nor Lua itself, because everything is being collected properly.
Maybe LÖVE isn't cleaning up all the mesh data when mesh objects are destroyed, or it forgets to free the vertex data in some cases? I can't tell what exactly is going on, so I'm just guessing at this point.
Who is online
Users browsing this forum: Ahrefs [Bot], Google [Bot] and 2 guests