views:

185

answers:

4

I've been thinking if there's a way how to speed up freeing memory in .NET. I'm creating a game in .NET (only managed code) where no significant graphics is needed but still I would like to write it properly in order to not to lose performance for nothing.

For example is it useful to assign null value to objects that are not longer needed? I see this in a few samples over Internet.

Thanks for answers!

A: 

To do this (and I've had to do this with frequent >50mb allocations), call:

        myObj = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();

I've noticed that the memory footprint of the app will then greatly diminish. In theory, you shouldn't need to do this. However, in practice, with a 32 bit windows OS you might get 2 contiguous blocks of >300mb free at any given time, and having that space taken up by lots of little allocations or a series of big ones can mean that other large allocations will fail unnecessarily. The garbage collector runs in the background when it can, but if you absolutely must make large allocations right now, that set of lines helps to make that possible for me.

EDIT: From what I put in the comments, for the downvoters.

If you read the entire post about garbage collection by Rico Mariani, you'll note that large, infrequent, non-predictable memory allocations fall into scenario #2. To whit:

Rule #2

Consider calling GC.Collect() if some non-recurring event has just happened and this event is highly likely to have caused a lot of old objects to die.

A classic example of this is if you're writing a client application and you display a very large and complicated form that has a lot of data associated with it. Your user has just interacted with this form potentially creating some large objects... things like XML documents, or a large DataSet or two. When the form closes these objects are dead and so GC.Collect() will reclaim the memory associated with them.

Now why would I suggest this as a possible time to call the collector? I mean, my usual advice goes something like "the collector is self-tuning so don't mess with it." Why the change of attitude you might ask?

Well here is a situation where the collector's tendancy[sic] to try to predict the future based on the past is likely to be unsuccessful.

If you make large allocations in your game, you will need to be careful about how memory is handled. The garbage collector works on prediction based on past events, and large blocks of memory on a 32bit machine can be devastating to future allocations if not properly managed. If you haven't done it, don't automatically assume that I'm wrong; if you have done it, I'd welcome an explanation of how to do it properly (ie, how to defrag memory to make sure I can always allocation 50-100mb of memory at a given time).

mmr
Why the downvote? If I don't do this, my app does not work on 32bit platforms, period.
mmr
http://stackoverflow.com/questions/118633/whats-so-wrong-about-using-gc-collect - From what I've read already it seems to be bad practice. (I didnt downvoted you. Thanks for an answer and +1)
MartyIX
I didn't downvote you, so can't answer, but would guess because it's generally considered bad practice to call 'GC.Collect()' - see http://blogs.msdn.com/ricom/archive/2003/12/02/40780.aspx
Dan Diplo
So, according to that question, my large allocations fall into the realm of scenario #2: "Consider calling GC.Collect() if some non-recurring event has just happened and this event is highly likely to have caused a lot of old objects to die.A classic example of this is if you're writing a client application and you display a very large and complicated form that has a lot of data associated with it. Your user has just interacted with this form potentially creating some large objects... things like XML documents, or a large DataSet or two."
mmr
This can actually make this _slower_ (see my answer for the reason why), and it's still not _guaranteed_ to collect your target object.
Joel Coehoorn
@Joel Coehoorn: I'd rather _slower_ than _dead_, which is what happens when I don't do it.
mmr
@mmr: Your extreme corner case really has nothing to do with common practice, though.
BlueRaja - Danny Pflughoeft
It's not *that* corner. Any application that implements a `File/Open` dialog to load a really large object model can encounter it, because that's something that the program isn't doing frequently enough for the GC to figure out. Manually kicking off garbage collection is certainly not the first thing you should think about, but it's useful to know how to do it.
Robert Rossney
+3  A: 

Most of the graphics related objects in .net (Image and its descendants, Graphics, Pen, Brush, etc) implement IDisposable. If you're going to be using an object for a specific operation, then not again, use the following pattern:

using(var g = Graphics.FromBitmap(bmp))
{
    //Do some stuff with the graphics object
}

Doing it like this will ensure any unmanaged resources will get freed when they fall out of scope.

Matt S
He's asking about memory, though, and using/IDisposable really has nothing to do with memory.
Joel Coehoorn
Calling `Dispose()` has everything to do with memory, when it's GDI objects (Graphics, Pen, Brush, etc) concerned. It only has little to do with managed memory that will be collected by the GC.
Steven
If he's doing doing manipulation of images, using the IDisposable interface will clean up the image handle and the associated memory.
Matt S
I don't use GDI or any other unmanaged code. WPF should take care of this if I'm not mistaken.
MartyIX
+5  A: 
Joel Coehoorn
Thanks for the answer! You said that assigning null is not useful. But does it mind anything?
MartyIX
@marty: It can hurt performance when you do it too late. And everywhere it's useful, it is also a standard optimization. So don't do it.
Henk Holterman
+1  A: 

I find allocating objects in .net one of the things that affects performance the most. To get around this I use the a Factory pattern where I recycle objects that I have used. Here is simple generic implementation:

internal class ListFactory<T> 
    where T: IRecyclable, new()
{
    private List<T> _internalList;
    private int _pointer;

    public ListFactory()
    {
        _internalList = new List<T>();
        _pointer = 0;
    }

    public void Clear()
    {
            _pointer = 0;
    }

    public int Count
    {
        get
        {
            return _pointer;
        }
    }

    public List<T> InnerList
    {
        get
        {
            return _internalList;
        }
    }

    //Either return T form the list or add a new one
    //Clear T when it is being reused
    public T Create()
    {
        T t;

        //If the pointer is less than the object count then return a recycled object
        //Else return a new object 
        if (_pointer < _internalList.Count )
        {
            t = _internalList[_pointer];
            t.Recycle();
        }
        else
        {
            t = new T();
            _internalList.Add(t);
        }
        _pointer ++;
        return t;
    }
}

For my line routing algorithm, I need to constantly keep many values as a RouteNode which implements the following interface:

public interface IRecyclable
{
    void Recycle();
}

These get constantly created and destroyed. To recycle these objects, create a new factory:

nodeFactory = new ListFactory<RouteNode>();

When you need an object, call the create method:

RouteNode start = nodeFactory.Create();
RouteNode goal = nodeFactory.Create();

When you have finished with the objects in the list, clear the list. The pointer is reset to the start of the list, but the objects themselves are not destroyed. In fact the Recycle method is only called when the object is reclaimed again. (You may want to do this earlier if you have other object references, see comments below)

This is a pretty naive implementation, but its a place to start.

James Westgate
I like that you've clearly figured out where the slowness lies in your application through measurement. I would be wary of using this technique with any class that contains references to other objects; you'd need to implement `IDisposable` and be sure to dereference those objects when returning your recyclable object to the free space list.
Robert Rossney
I wouldnt dream of using this with IDisposable but if you are referencing other objects you could get yourself into a mess quite quickly - agreed.
James Westgate
Just to add to this - the Recycle method is used to set any external references to null, thus allowing normal garbage collection to work for any referenced classes.
James Westgate
@James, Do you have any numbers to show the speed improvement when you switched to this method? I'm just having a hard time believing that this is what actually what is saving you so much time (Obviously, I'm open to being proven wrong).
tster
I was creating thousands of these nodes per minute and after a while you just plain run out of memory and the os has to start paging to disk. Forcing GC collection wasnt an option becuase this would create intermittent slowdowns. I have found allocating memory one of the slowest parts of managed code and I try where ever sensible to use an approach where objects are kept active and reused.
James Westgate