Patterns for reducing memory usage

time to read 4 min | 610 words

Memory problems happen when you application use more memory that you would like. It isn’t necessarily paging or causing OutOfMemory, but it is using enough memory to generate complaints. The most common cases for memory issues are:

  • Memory leaks
  • Garbage spewers
  • In memory nuts
  • Framework bugs

Let me take each of them in turn.

Memory leaks in a managed language are almost always related to dangling references, such as in a cache with no expiration or events where you never unsubscribe. Those are usually nasty to figure out, because tracking down what is holding the memory can be unpleasant. But, by the same token, it is also fairly straightforward to do so.

Garbage spewers are pieces of code that allocate a lot of memory that will have to be freed soon afterward. A common case of that is:

public string Concat(string[] items)
{
   string result = "";
   foreach(var item in items)
      results += item;
 
   return result;
}

This is going to allocate a lot of memory, which will have to be freed soon after. This will get cleaned up eventually, but it will put a lot of pressure on the GC first, will cause the application to consume more memory and in general won’t play nice with others. While the code above is the simplest way to explain this, it is fairly common in ways that are harder to detect, a common case would be to load a DTO from the database, convert that to an entity and convert that to a view model. Along the way, you are going to consume a lot of memory for doing pretty much the same thing.

Now the caveat here is that most objects are actually small, so you don’t really notice that, but if you are working with large objects, or a lot of them, this is something that is going to hit you.

In memory nuts refer to a common problem, you simply put your entire dataset in memory, and commonly refer to it by direct model traversal. When your dataset becomes too big, however… well, that is the point where the pain is really going to hit you. Usually, fixing this is a costly process, because your code assumes that the entire thing is in memory. Even if you can easily save it to persistent storage, fixing all the places where the code assumes that everything is just a pointer reference away is a big problem.

Framework bugs are my least favorite, it is when you run into cases where the framework just won’t release memory. Most often, this is because you are doing something wrong, but occasionally you will hit the real framework bug, and tracking that down is a pure PITA.

In all cases, you need to set up some goals, what is acceptable memory usage, in what scenarios, over what time frame, etc. Then build test scenarios that are repeatable and try each of your improvements out. Do not try to implement too much upfront, that way lies the road to madness.