How do you delete a ModelAnimator or Model...?

Topics: Developer Forum
Mar 5, 2007 at 6:50 PM
Summary: I'm having trouble deleting a ModelAnimator instance. In a multiple-dwarf demo, I can't get rid of dwarves that I don't want. Forgive me, I think this question probably has a very obvious answer, but I can't see it.


Background:

I'm doing a client/server multiplayer PC demo using the dwarf model and the ModelAnimator class. I've wrapped up the ModelAnimator functionality in my 'playerCharacter' class, and load the Model, AnimationController, etc, providing separate world matrices for each dwarf. In my game class I have a Dictionary<int key, playerCharacter> that keeps the list of dwarves, one of which is controlled by the current client, the others are controlled by the server. That all works wonderfully, I can add many dwarves and run them around the world, and the clients stay synched with the server.

The problem comes when I shut down one client, so that a dwarf should disappear from all of the other clients. On each client, I remove the deleted dwarf from the Dictionary, and I set the ModelAnimator and Model member variables of the to-be-deleted playerCharacter instance to 'null'. However the deleted dwarf does remains on the screen.

I'm new to C#, .NET and XNA. How does one 'delete' stuff in C#? It seems that you're supposed to just quit referring to an object and 'let' the system garbage collect it. So I've 'nulled' my ModelAnimator, but the character won't go away. Is there some way to explicitly delete it?


Thanks,

Mike
Mar 5, 2007 at 6:56 PM
Oops...is it as simple as setting Enabled = false on the base DrawableGameComponent? Will that get rid of everything from memory, or does it just hide the model?

Mike
Mar 5, 2007 at 7:11 PM
OK, more info. The ModelAnimator constructor does a 'collection.add' to the game.Components collection, but there doesn't seem to be any code that does the 'collection.remove'. Does the game.Components get automatically cleaned up if I've called 'dispose' or 'enable=false' on a Component that is in the collection?

public ModelAnimator(Game game, Model model) : base(game)
{
this.model = model;
...
game.Components.Add(this);

}

Mike
Mar 5, 2007 at 10:26 PM
More conversation with myself...

It seems to work if I do the following:

dwarfAnimator.Enabled = false;
dwarfAnimator.Dispose();


But I'm not convinced that there isn't more to it. The system prevents me from calling Dispose(true), which would 'release both managed and unmanaged resources; (false to release only unmanaged resources.)'. However, calling 'Dispose()' only releases unmanaged resources...so am I ever releasing the managed resources, or not?

Thanks,

Mike
Mar 6, 2007 at 1:02 AM
Hi mikeS,

mikeS wrote:
However, calling 'Dispose()' only releases unmanaged resources...so am I ever releasing the managed resources, or not?

I checked it with the debugger.

When the Game instance is disposed, Dispose(true) of all GameComponents is called. This timing is Game.Exit(). But, when I called Game.Components.Clear(), Dispose() is not called.

I don't understand C# as well as you. But, normally, we, I and you don't need to release memory clearly. GC(garbage collection) deletes our instance which is referred by nobody. Therefore, if your managed resources are referred by nobody, those resources will be removed at some future time.

In Cpp, we can delete unrequired instance immediately. But, in C#, we might have to wait that GC tries to delete an instance. The only thing we can do may be to trust GC.

P.S.
I tried to watch the timing which GC tries to clean-up. But, I could not watch it well. You can access GC with System.GC static methods.

Sorry for my poor English,
minahito
Mar 6, 2007 at 3:03 PM
Hmmm, thanks. I suppose I should have a look at the timing in the debugger. I'm not thrilled with being uncertain about when the memory gets released, I think I'm missing something about how to tell when an object is not being referred to by any other object. I'm concerned that an application that creates and removes assets frequently will run out of memory if I accidentally continue to refer to some of the objects that should be destroyed.

BTW, I'm using .NET remoting for my client/server code, and so far it works nicely. It's not useful on XBox360 AFAIK, but on PC it seems promising.

Thanks,

Mike
Mar 7, 2007 at 2:05 AM
Edited Mar 7, 2007 at 2:06 AM

mikeS wrote:
I suppose I should have a look at the timing in the debugger.

I don't know whether this way is correct. :D
I thought it's may be good to put break-point into destruction for watching GC timing. But, I could not do it well.

I'm not thrilled with being uncertain about when the memory gets released, I think I'm missing something about how to tell when an object is not being referred to by any other object. I'm concerned that an application that creates and removes assets frequently will run out of memory if I accidentally continue to refer to some of the objects that should be destroyed.

Me, too. I'm afraid of memory-leak. But, GC system of .NET & C# exists to solve memory-leak automatically. So we should believe their working, even if we can not watch it. However, Generics Vector Array (List<> and others) may become the cause of memory-leak. I think we should pay attention to it a little.

P.S.
Thank you for interesting tips. I'll try to use .NET remoting.
Mar 7, 2007 at 2:43 AM
Edited Mar 7, 2007 at 2:55 AM
Sorry, I wrote incorrect things.

I found documents about it.

When we call Dispose(), an instance is removed immediately by GC. because Dispose() calls Finalize which is like destructor of Cpp. In a word, an instance which implements IDispose interface is removable clearly as well as Cpp. This is C# language spec. I found translated documents about this which is written by Bobby Schmidt, in Japan MSDN. I want to point URL of its original document, but I could not find it in MSDN English site...

Bobby Schmidt said, Dispose is the idiom which prevents calling finalizer two times or more.

Cpp
delete *dwarfAnimator;
delete *dwarfAnimator; <--- Error

C
dwarfAnimator.Dispose(); <-- call finalizer
dwarfAnimator.Dispose(); <-- don't call finalizer

And, because "finalizer" is not the same as Cpp's "destructor", it has been named different name from Cpp.

I could not find the same document, but I think the following URL is useful for you (and me);
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconProgrammingEssentialsForGarbageCollection.asp

The following two programs has the same step;

Cpp
Foo *foo = new Foo();
delete *foo;

C
Foo foo = new Foo();
foo.Finalize();

If Foo class implements IDispose, we should use Dispose() instead of Finalize().

C
Foo foo = new Foo();
foo.Dispose();

We can delete ModelAnimator clearly. Also, we may be able to delete Model with Finalize(), if we need.
Coordinator
Mar 7, 2007 at 3:16 AM
Edited Mar 7, 2007 at 3:16 AM
Short answer:
Say I have these variables:
Game game;
Model model = content.Load<Model>("MyModel");
ModelAnimator animator = new ModelAnimator(game, model);

To both stop the animator from drawing and mark it for garbage collection, you can do:

game.Components.Remove(animator);
animator=null;

Remember that the resources used by Model will not be disposed until either the game ends, or you call model.Dispose(), or you call content.Unload()


Longer Explanation:
You can allocate things malloc and free style with .NET if you use unmanaged code. There is an example floating around there that shows you how to set up static methods that function exactly like malloc and free. The thing is, doing manual memory management is almost always unneccessary, so the garbage collector is a real treat.

Anyways, as you have figured out, Dispose() only releases unmanaged resources. An object is marked as able to be garbage collected if there are no references to it that can be accessed at a later time in the program. ModelAnimator uses no unmanaged resources other than resources used by the model, which is expected to be disposed of by the user, so calling dispose on a ModelAnimator will do nothing. The ModelAnimator registers itself as a GameComponent in the Game.Components collection, so it will not be garbage collected until it is removed from the components.