Managing Memory in Solana Programs

Blockiosaurus
Metaplex
4 min readJun 14, 2024

--

It looks like solana-program 1.18 has some new breaking changes that cause stack overflows to occur more frequently. Here’s a refresher on how memory works in Solana programs and how to reduce the likelihood of this error through proper program memory management.

Program Memory Overview

The Solana Runtime has three memory types; Stack, Heap, and Account space.

  • The stack is meant for local variable storage and is where most simple data types are stored. There is 4KiB of stack memory, but generally each function gets its own 4KiB allocation.
  • The heap is meant for more dynamic storage. Vecs and Strings are stored here and it’s meant for larger data. Programs have 32KiB of heap space, but that allocation is used for the entire program.
  • Account space is a direct mapping of Solana accounts and persists across calls to a program. If the Stack and Heap are volatile RAM, account space is the hard drive where the data is written back to. This memory can be used directly via zero copy serde libraries, but that’s out of scope for this guide.

What to watch out for

The Rust compiler is pretty smart, so if you try to allocate too much stack space in your program, it’ll let you know at compile time with a warning.

If you ignore this and violate the stack size limit, you’ll receive a runtime error.

This means your instruction is exceeding the 4KiB of stack by trying to allocate too much space. This usually happens in large, complicated instruction handlers that use a lot of local variables.

How do I fix it?

Split your code into Functions

If your instruction handler is a single, long function that declares a lot of variables, the solution may be as simple as splitting out some of the code sections into its own section.

Each function call in the Solana Runtime is given its own stack frame, which means it has its own 4KiB stack allocation to use. This means that if you’re exceeding the stack size in a large function, pulling a section of the code into a sub-function will provide it with a new 4KiB memory stack to work with. There are some limitations to how many times you can do this, and adding sub-functions causes minor performance hits, but for most use cases it’s unlikely that either of those scenarios will be reached.

Box it

Oftentimes the easiest solution for Anchor programs is to simply Box an account, as shown below.

When an Anchor Account includes a type, it tries to deserialize that type at the beginning of an instruction. By default this deserialized account is stored on the stack, and for large accounts this can quickly use up all of the Stack space. By boxing this account, you’re telling the compiler that you want this deserialized account to live on the heap instead. As stated above, there’s 32KiB of heap space to work with and most of the time it’s underutilized. Switching account data to be stored here instead can be a quick and easy solution to preventing stack overflows.

Custom Heap Allocator

The downside of using the heap is that there are only 32KiB to work with for the entire program, and unlike the stack it’s gone once you use it all. So once you’ve exhausted all of the stack space and all of the heap space, you’re out of luck. But what if I told you that there’s a way to recover heap space after you’re done using it?

When the Solana runtime was first developed the expectation was that Solana programs would all be simple, short, and straightforward. As such the default heap allocator, which is what manages the program’s heap space, expects to never need more than 32KiB of data and so doesn’t de-allocate the heap space after the program is done with it.

If you’re reading this, obviously you know that expectation hasn’t panned out. Luckily there’s a way to write a better heap allocator that knows how to clean up after itself. Solana Labs even has an example in the SPL (https://github.com/solana-labs/solana-program-library/tree/master/examples/rust/custom-heap). With a custom allocator the 32KiB heap space no longer becomes use-it-and-lose-it memory, and you can keep reusing the space for additional variables after previous ones are de-allocated.

Hopefully some combination of the solutions above can help you resolve any Solana program memory issues you’re having. If you have any additional questions, don’t hesitate to reach out to me on Twitter!

--

--