Memory leak from sprites that are never added

SDK 2.7.3, device running OS 2.7.3

If (in lua) I create a sprite from an image, then drop all references to it without ever calling :add() or :remove() on it, the memory taken up by its image never gets freed.

This can be reproduced on device with the following sample code and watching the memory usage in Device Info:

import "CoreLibs/graphics"
import "CoreLibs/sprites"

local gfx <const> = playdate.graphics

function makeBigSprite()
	-- make a really big image so the problem is obvious on the memory graph, this uses around 8mb
	local bigImage = gfx.image.new(8000, 8000, gfx.kColorBlack)
	return gfx.sprite.new(bigImage)
end

bigSprite = makeBigSprite()
bigSprite = nil

function playdate.update()
	gfx.sprite.update()
end

I’d expect to see a spike in memory usage when calling makeBigSprite() that would immediately be freed by the garbage collector after setting the bigSprite variable to nil.

What I actually see is ~8mb in use forever.

If I call bigSprite:add() followed by bigSprite:remove() before setting it to nil, the memory does get freed as I’d expect. But I wouldn’t expect the SDK to be holding on to a reference to the sprite or its image if I never call add() on it.

The use case where this matters is object pooling. If I create a pool with a bunch of ready-to-use sprites, some of them might never be needed, and therefore never have add() or remove() called on them.

3 Likes

My partner and I just identified the same problem in our game yesterday. We were creating some sprites on load that didn't always need to get used, and it led to a large memory leak over time.

We are going through now and adding some cleanup functions to make sure remove gets called even when we never add them, but we also did not expect to need to do this.

We were already calling gfx.sprite.removeAll() in an attempt to clean everything up on scene transitions, but it seems you have to explicitly call remove on the individual sprites in this case.

this is fixed in 2.7.5! yay!