Hi everyone
I'm developing a Python application that I'm compiling into a WebAssembly component using componentize-py and running it with the wasmtime runtime. My application includes a Server-Sent Events (SSE) client, which means it needs to run continuously and process a steady stream of incoming data.
The challenge I'm facing is memory growth. I'm observing that the memory usage of my Wasm component continuously increases over time, even though I'm trying to manage objects carefully in my Python code. The memory growth seems to be due to the wasi resources not being released.
I'm relatively new to WebAssembly and am trying to understand how memory is managed in long-running Wasm components. Are there known solution for this type of memory growth or is this a current limitation?
@Joel Dice might be best here at first glance....
In general memory management isn't that much more special in wasm than it is on native, so likely what you're running into here is a memory leak. Now tracking down memory leaks is pretty different on native, and at this time unfortunately much more difficult.
If you're able to make a small reproduction that'd be super helpful for something like this to investigate
For example, when I create a Fields resource from wasi_http_types and later my code moves on and no longer needs these fields, the resource still appears to stay in memory. This seems to be the case for other resources from WASI as well. It does not get freed as expected after it goes out of scope. This is in contrast to typical Python objects (like lists or dictionaries) which are correctly garbage-collected.
In my SSE client, data is continuously read from an InputStream resource from wasi-io. Each time the read function is executed, the data that has been read also stays in memory. So over time, as the SSE client keeps receiving data, the memory keeps increasing until a MemoryError is received.
Here's the relevant code snippet:
incomingBody: wasi_http_types.IncomingBody = self._response.consume()
with incomingBody:
inputStream: streams.InputStream = incomingBody.stream()
with inputStream:
while True:
try:
pollable: poll.Pollable = inputStream.subscribe()
with pollable:
pollable.block()
chunk: bytes = inputStream.read(chunk_size) # All data read here is kept in memory and does not get released
yield chunk
except ...
ok nice, and makes sense, thanks!
Mind opening an issue on componentize-py for this? Joel's out this week so it'd be good to write these details down on a repo as well
Last updated: Dec 06 2025 at 06:05 UTC