Are memory leaks an issue with C#?

Can any C# experts provide some feedback on memory leaks with C#? Are they an issue?

This was always a problem with AS3 because of poor coding practices and garbage collection.

A memory leak in C# generally refers to some references which aren’t cleared, which allows objects to be reached from the program root. This prevents objects from being collected when they are expected to be. This is very much a problem.

The traditional memory leak, where some memory is allocated, then all references to it are lost, is pretty much impossible in C#.

Technically speaking, a traditional memory leak is not exactly possible in either AS3 or C#.

A memory leak is a memory allocation that was never deallocated, but there is no operating code left that has reference/points at said memory. It is now lost memory that can’t be deallocated with out completely closing out the program/process (signaling to the OS to deallocate all memory used by that program/process).

In a managed language like AS3 and C#, all allocations are tracked by the memory manager, if no references exists (aside from the memory manager’s reference) the memory is automatically deallocated.

Now with that said… you can get into pickles with managed languages still, causing run away memory usage… that is similar to a traditional memory leak. And is sometimes called a memory leak.

This is an instance where your code just logically doesn’t bother to deal with existing references. See, if a reference still exists somewhere in the program, the memory manager won’t free up that memory. And if you have some code that doesn’t bother to drop all references, it can get stuck.

This happens in AS3 a lot with the addEventListener function. Many people will add a listener and then never remove said listener. The thing is because of the way the AS3/Flash runtime dealt with function identity, the class the function is attached to is kept around for that listener. So you get an issue like this:

var obj:CustomSprite = new CustomSprite();
this.addEventListener(MouseEvent.CLICK, obj.ClickHandler);
this.addChild(obj);

//then later on we try to remove it, but don't unregister the event
this.removeChild(obj);
obj = null;

Here the CustomSprite persists, because technically a reference exists to it, in that the event listener is a reference. The garbage collector will never purge the CustomSprite from memory, despite it no longer being displayed. And since your logical code doesn’t have a reference, there’s no easy way to remove the event listener.

This is the result of bad coding practices, as you described.

And yes… similar scenarios can arise in C#. Where though depends on the structure of some API you might be programming against. I can’t think of any really big stinkers off the top of my head… but yeah, be aware run away memory could occur if you code poorly.

Really though, the big ones that happen aren’t necessarily leaks. But that you end up allocating so much memory that the ‘heap’ grows really big, and then doesn’t shrink because Mono isn’t sure if you’re going to need it again or not.

See how the Mono runtime deals with managed memory is that it asks the OS for a large chunk of memory. It uses this memory to place various objects in. Every once in a while it goes through and tosses out objects that aren’t needed… but holds onto the memory from the OS. It doesn’t bother fully deallocating it, because it’ll probably be needed again soon. This is why mono’s memory footprint may be larger than the actual memory needs of the program at any given moment.

This memory will of course get fragmented over time. You create 8 objects that take of a block of contiguous space, than you delete 2 of them that happen to be in the middle. Now there’s a small bit of memory in between 6 objects not being used. These pile up and is called ‘fragmented memory’. The memory manager will occasionally repack the memory space (think like defragging a harddrive back in the day) cleaning up the spaces in between.

This is why we say ‘references’ in C# aren’t exactly like ‘pointers’ in C++. It’s technically not a pointer to a specific location in memory… since that location in memory could move at any moment because the memory manager decided to defragment the heap.

Anyways… now lets say we go an ask to create a very large array. Taking up several megabytes of contiguous memory (arrays take up contiguous memory blocks). Problems may arise if you’ve used up most of the memory in the heap at the point of creating this array… OR if the heap is very fragmented and there’s no where to fit it despite there technically being freespace enough (like a full tetris screen with holes in it). OR lets say you’ve recently created an extremely large array, and then you go and create a 2nd, and a 3rd yet dereference the previous ones… but the timing is such that the GC hasn’t run yet.

Well! The memory manager must ask the OS for MORE memory. But then afterward the memory gets cleaned up, GC throws out stuff, it gets defragged, and now your memory usage is back below the initial memory allocation. Well… it’s already asked the OS to grow… and it just holds onto it.

This could easily happen if you go and load up a VERY LARGE scene, that has a lot of scripts that do things like manipulate the vertex arrays, or the textures, of multiple gameobjects on scene in the Update method. You’re going to be creating tons of garbage this way that allocates huge amounts of memory. This chews into a huge amount of system memory… then you move on to a new scene! Thing is… mono still holds onto all that memory, thinking that you might need it for something later on. Sure it’ll be recycled… but if it’s allocated 2 gigs of memory because this one scene did a butt ton of manipulation of large arrays, and then after that you only ever used 256MB of memory for doing much of anything… mono still has 2 gigs of memory allocated for recycling.

This too is often seen as a ‘memory leak’. Though technically isn’t because the memory is still usable by the program, it’s just no longer usable by the system. (similar things happened in unmanaged languages, where the code attempts to recycle, but bloats up anyways… browsers like Google Chrome are huge culprets of this).

tldr;

Memory leaks in a technical sense aren’t really a thing in C# (though the runtime itself could have leaks). But things similar to it do exists.

In the end, smart/aware design should be practiced. But really only become a huge hassle if you’r doing things in the millions of times, or when dealing with large memory consumers (like huge arrays).

This is why the .net framework tends to favor ‘memory streams’ over large objects. But unfortunately Unity doesn’t uncover things as memory streams, and prefers arrays… (like vertex arrays and the sort)… and especially loves duplicating them when you access them. ::rolls eyes::

4 Likes

The things I’m not sure about are Materials, Meshes and Textures which you create yourself (new Mesh(), and so on.) I’ve seen mentions that you may have to manually tell Unity to free some of these, or else have them build up as (essentially) garbage.

1 Like

Yes, the unmanaged stuff like Materials, Meshes, and Textures… those come into a whole new ballpark. But that’s because they exist on the Unity side of things, and not in C#.

The C#/mono side only really has a wrapper around those objects, grants you limited access to them. Like how you can get a copy of the vertex array of a Mesh.

This even goes (on a smaller memory scale) for things like GameObjects and Components which have an unmanaged side to them in unity as well. Hence the existence of the ‘Object.Destroy’ method.

The other thing you have to be careful about with memory is triggering the GC. In a moderately complex scenario, a GC collection can take more the 15 ms, causing you to drop a frame. Do that a lot, and you game will appear to stutter.

So just because you can’t have a full scale memory leak, doesn’t mean you should ignore memory considerations. Managed languages come with a performance cost.

2 Likes