Sign in to follow this  

[.net] Forgive my ignorance but shouldn't it implemented a reference counter?

This topic is 4302 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I was trying C sharp and after falling in love with its features (like how easy using SDL.net became) I am having some problems memory management aside. There is a garbage collector which means that it will wait till you are using a lot of memory and then run a long process looking for objects that can be destroyed. Let me be worried about efficiency because of that, but well I thought that if I could make sure to release references to objects I won't use I could keep the memory usage low so the garbage collector would be seldom executed or maybe when a level ends or when a level is loaded - moments where the garbage collector wouldn't be considered. The problem is that I can't really make endure to let it destroy objects objects I won't use again. But well the point of this thread is to ask about the behaviour of this code:
using System;

namespace destructortest
{
	class Employee: System.Object [IDisposable]
	{
		
	    string m_name;
	
	    public Employee(string name)
	    {
	        m_name = name;
	    }
	
	    ~Employee()
	    {
	        Console.WriteLine("Employee destructor executed.");
	    }
	}
	
	class MainClass
	{
		public static void Main(string[] args)
		{
			Employee e=new Employee("whatever");
			Console.WriteLine("C");
			set e=null;
			Console.WriteLine("B");		
			Console.ReadLine();
		}
	}
}


the Employee Object created there is only pointed by the e variable and I eventually set e to null. According to my logic the framework should note that there is nothing pointing to that employee and free the memory . But it doesn't, the object is destroyed once the program ends. It could be that programmers may not like the object to be destroyed there immediatelly, still I doubt they would preffer that object to be destroyed along with 1000 other objects by the garbage collector. I thought that there could be a way to ask the garbage collector to clean an object if possible, but well it seems that it is only possible to make the garbage collector collect all the memory used and the only option is to let you choose the generation of the object. So I would like an explanation for this behaviour or a suggestion for alternatives. And yes, I am a C++ junkie.

Share this post


Link to post
Share on other sites
There is a special pattern to ensure disposal of single objects: dispose pattern

In general you should avoid dispose routines as they have the most unusable semantics you can imagine. Debug output might be ok, RAII for ressources external to .NET is ok too, anything more is asking for trouble.

Share this post


Link to post
Share on other sites
if you really want that sort of destructors, then you shouldnt use .net.
use unmanaged c++ or something.

the whole idea of a garbage collector is, that you dont have to care about freeing memory. the garbage collector does it for you.
you can use GC.Collect() to force a garbage collection, but this will hurt performance.

Share this post


Link to post
Share on other sites
dispose would only help me to dispose disposable elements used by the object and unmanaged stuff.

I don't think it would work for the memory took by 'normal' attributes.


Edit: Seems I was wrong let me check more into it.

Cody: I do not think garbage collecting should be considered the main feature of .net I actually preffer C# for the API, the syntax and mainly .net assemblies, using DLL is no longer a pain in the ass.

And I would trust GC for some objects what I want to avoid is to let it become full of objects that are easy to get rid of.

Share this post


Link to post
Share on other sites
There is a simple reason why stuff isn't collected instantly: It's too expensive to do that.

Automatic memory management always comes with a price, in reference counted languages it's memory overhead for the counter and runtime overhead for updating the counter. In GC-languages the price is that you waste some memory with dead objects.

Share this post


Link to post
Share on other sites
Why do you care if your application hasn't freed up the memory at a particular point? The idea of garbage collection is that you don't have to worry about freeing things, and when your memory usage becomes too high you are cleaned up after.

Quote:
I think that if you set to null .net should not that there isn't anything pointing to that object and get rid of it

It is not so simple:
Object o1 = (something);
Object o2 = o1;
o1 = null;


Quote:
I guess .net ... has no future in games

I don't see why not having direct control over memory makes it unusable for games.

Share this post


Link to post
Share on other sites
seems I took too much time to remove that line so I 'll have to deal with it.

Under the theory that you were forced into filling your memory up and then the garbage collector will be executed leads to 2 things:

* memory is increasing up with no reason and constantly
* Garbage collector will be executed when you don't want it to be executed, like in the middle of your game and cause a freeze if you are using a lot of memory.

Just saying that If you could manage a little of memory you will be able to improve performance. And a way to improve performance would be removing an object once there is no variable referencing to it automatically. Just a theory.

But now that I saw the dispose pattern I guess I might check more about it.

---

Quote:
Object o1 = (something);
Object o2 = o1;
o1 = null;


A simple reference counter.

o1 = new whatever.

The (new whatever) has reference counter increased (1)
o2 = o1 the object pointed by o1 gets reference counter increased (2)
o1 = null the object pointed by o1 gets reference counter decreased (1)

so 1 is not 0 and it would not clean anything

until you set o2 to null.

And it is just a simple thing, for most complex things like circular references (where just counting wouldn't note anything) it won't do anything and you will have to rely on the GC.

The question was: Why the lack of an "auto destroy object when nothing is referencing to it" system?

Share this post


Link to post
Share on other sites
the Garbage colector doesn't run when a variable runs out ou scope, or when is set to null it runs when a certain amount of memory (1Mb curenty) has been alocated (Runs level 0 colection which means it only walks the stack and heap starting from top level stack frames to see wich references have been nulled out) so if you want a see a garbage colection happen try alocatin more than 1 MB of memory.

Second the GC doesn't run on the same thread with the code that alocates the objects so the destruction of objects takes place without interupting the program flow. (for GC collection lev 0 and 1)

But the GC can pause the main thread when a memory compacting operation occurs because it needs to reset the references to the new memory locacations after compacting. So if you're going to have the memory compacted it means that you reached the limit wich means also the swap file comes into play so the big performance penalty here comes from swaping and not from memory compacting or gc collections

Share this post


Link to post
Share on other sites
Well then I guess 1MB is not too much. And it executes in another thread, it shouldn't be much problem I guess.

But what if you actually have a lot of objects that are still being used and take a lot of memory? wouldn't it cause the GC to execute periodically without any success?

Side note: I noted some classes in the API are actually disposable, that's something you don't hear about so often, so you might actually have to control some memory usage anyways.

Share this post


Link to post
Share on other sites
Quote:
Original post by Vexorian
But what if you actually have a lot of objects that are still being used and take a lot of memory? wouldn't it cause the GC to execute periodically without any success?


Actualy the CLR is optimized for lots of small obect allocations with short lifespan, and is also otimized not to take GC perf hits based on the number of objects present in memory. To put it another way if you have a milion objects alocated and you alocate 1 thousant more the perf is the same almost as having no object alocated and alocating a thousant. This is accomplished using object generations. So objects that live through one garbage colection are ugraded to level 1 obects, wich means they wont' be walked or counted when generation 0 collections occur.

Anyway it's better not to have to many allocations in the middle of the progragam runing but rather at initialization time. No alocations also means no GC's will ocur so all the proc cycles are for your use only ;-)

Quote:
Original post by Vexorian
Side note: I noted some classes in the API are actually disposable, that's something you don't hear about so often, so you might actually have to control some memory usage anyways.


the Disposabe pattern is in DotNET for releasing resources (files, handles, unmanaged memory, etc) not memory. You can't release managed memory using disposable object but you can release resources in a deterministic way as oposed to gc finalization (non dedetrministic : time and order of finalisations can't be determined acurately)

Share this post


Link to post
Share on other sites
Quote:
Original post by Vexorian
The question was: Why the lack of an "auto destroy object when nothing is referencing to it" system?

Because the garbage collector knows what its doing. The whole idea is that you don't worry about garbage collection in general, you just get things working. By adding a second type of garbage collection, you greatly increase the complexity of the programs, which removes the entire reason for it to exist in the first place.

Also, keep in mind that deallocation isn't free. Whenever possible, you want to deallocate lots of memory all at once, it stands to make later allocations cheaper.

CM

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Well, I guess it is possible to implement a reference counting & releasing object by using unsafe code...

But I am sure you are better of with assuming the GC is smarter than you ;)

Share this post


Link to post
Share on other sites
Also, the way .NET is built, you can't simply collect a single object and leave it at that. An entire generation needs to be resolved at once.

Share this post


Link to post
Share on other sites
Also realize that C# ~MyClass() is not the same as a C++ ~MyClass(). The former is a finalizer, and results in the object getting queued up and the finalizer executed in a separate thread at an undetermined time. A C++ destructor, in contrast, is executed in a much more deterministic manner (at the end of scope, upon delete, etc.).

Read this excellent MSDN blog post by Rico Mariani for more information.

Quote:
the Employee Object created there is only pointed by the e variable and I eventually set e to null. According to my logic the framework should note that there is nothing pointing to that employee and free the memory .

But it doesn't, the object is destroyed once the program ends.


Based on the blog post, the mere act of adding a finalizer will cause the Employee object to be promoted to the next generation, which is collected less frequently.

If you're truly having performance problems (i.e. you're experiencing actual slowdowns while playing your game), use CLR Profiler or some other performance measuring tool to find out where the problems are. Don't waste time guessing. Instead, have fun coding :D

Share this post


Link to post
Share on other sites
A couple more comments, and I'm sure you're not going to like this :)

From my understanding one way to write games under .Net is to avoid having the garbage collector coming up at all. Tricks involve keeping things in the stack by using unboxed structures rather than reference types, and reusing allocated reference elements rather than allocating new ones.

I remember a while ago in VB 2002 that I was scratching my head trying to figure out why didn't the garbage collector clean up an object from which I had removed all references to from my code and then called gc.collect (with different generation levels, and several times)

It turned out that an optimization had created a copy of the variable that had originally pointed to the object, and that copy was just being allowed to fall out of scope and stack at the end of the method, but obviously I had code in the same method waiting for the destruction of the object :).

Basically, this is hard to accept, and kind of a philosophical step, but it seems to be easier to accept garbage collection as not being predictable, and code accordingly, expecting either slowdowns (which can be acceptable in non real time games, such as board games), or avoiding it at all.

Share this post


Link to post
Share on other sites
who has seen actual freezes in a game when the garbage collector runs?

i have not, and the gc runs quite often in my game due to lazy coding.

Share this post


Link to post
Share on other sites
Quote:
Original post by Arild Fines
Quote:
Original post by Vexorian
I am having some problems memory management aside.

Are you actually having problems or do you just think you have problems?


It seems my language module was not working at all yesterday, the thread's title is senseless and I actually said that.

What I meant in the beginning is that I loved C# features but I was having problems to understand/like its memory management.

After some explanations I was convinced that it was not such a bad problem as I thought.

cody: I have seen it happen in one of the first .net demo programs I've seen. It was one of SDL.net demos which dealt with GUI and fonts, in some of its section moving fonts caused a very large memory usage and eventually the demo kept freezing and freezing constantly.

I now think that it was a problem with the demo's code and that it should be possible to avoid those.

I am into 2D puzzle games anyways so I don't really need the fastest performance ever. My prediction is that .net will keep getting faster and it would probably be much faster in vista which everybody will have to get because it is going to be the only OS with directx 10

Share this post


Link to post
Share on other sites
The garbage collector initially reminded me of the C= 64 garbage collection. I remember every BASIC game that ran would always lock up at some point or another as the C= tried to free memory.

The C= 64 was a 1Mhz machine with 64Kb of memory, roughly 32Kb of it available for use. Garbage collection was annoying, but bearable.

Now we have 3.4 Ghz machines with 2Gb of RAM. OK, the increase in RAM is substantial, but so is the increase in processor speed. As well the additional resources allow us to maintain state information about the memory that we're collecting.

All in all, I've never seen garbage collection be a problem.

What I find is a lack of understanding on how garbage collection works in .NET. Most software will never feel a garbage collection occur. (They will occur, but they will be done quickly and efficiently.) The garbage collector in .NET maintains three "generations" (categories) of objects: gen 0 is short lived objects, usually with method scope; gen 1 is longer lived objects, possibly class scope or static scope, for instance; gen 2 is long lived objects.

Generation 0 objects are going to be declared and reclaimed almost immediately. It should be noted that the memory taken by these objects does not return to the host operating system! The memory is held by the Framework for its possible future use. This is important because some folks like to watch their resource meters to determine memory usage... using that method, software running within the Framework will seem to do nothing but eat at memory continuously.

Generation 0 takes less than 10ms to process. Generation 1 takes between 10ms and 30ms to process. Generation 2 processing time will be determined by your application and its usage profile -- but it is the generation most folks will "complain" about.

In order for an object to hit gen 2 it has to remain in use for a very long time. That means that gen 2 objects are the objects in continuous use in your application - basically, the only memory that could possibly be freed wouldn't be freed because quite possibly, your application is still using that memory. Whatever you're doing to use memory at this point it's time to take a step back and examine what you're doing and why.

Or, in order to hit gen 2, you can simply declare an object that falls into the gen 1 category and provide a finalizer for it. A finalizer automatically moves the object into the next category because the garbage collector doesn't know which portion of the object's state will be needed for the finalizer to execute. i.e. Wantonly adding finalizers to your objects to obtain a sense of control will only make things worse. Much worse.

Pinned objects are the bane of the garbage collector - and I've seen quite a few managed games using them. A pinned object cannot be moved or manipulated by the GC; the GC is forced to work around them. This isn't a good thing. Stay away from pinned objects.

There's also the matter of the Large Object Heap, but I doubt that many objects being used within a game qualify for instantiation there.

None of the above really come into play until the Framework has detected the need to compact the used memory, i.e. the system has hit a low memory condition.

In the end there's as many techniques and tricks for managing and optimizing the allocation profile of your application as there are to speeding up memory allocations within C/C++. It requires an investment of time and effort in order to understand the collections, when they occur, why they occur, what an allocation profile is, etc.

You're also able to force the garbage collector to do a collection, if you so desire. It's been stated numerous times by Microsoft (and by Mono team members) that this is a Very Bad Idea - but if you think you know better, feel free to do a GC.Collect().

On the topic of finalizer vs. destructor: it definitely should be understood that a finalizer is not a destructor. A destructor is executed as soon as the delete operator is used on an object. The finalizer may be called immediately or five hours from the time the application destroyed the object. Implementing the Disposable pattern simply provides a way to inform a developer using your code that you have unmanaged resources (or managed resources that are limited) that should be manually disposed of before discarding the object. When you examine the Disposable pattern and its proper implementation you'll see that there is indeed functionality in place to determine if your finalizer is being called from user code or from the garbage collector.

In regards to reference counting vs generational mark and sweep: Wikipedia has an excellent article on garbage collection as a form of memory management at http://en.wikipedia.org/wiki/Garbage_collection_(computer_science). I'd recommend it if you're interested in how the garbage collector works and why generational mark and sweep might be considered more efficient (long term) than reference counting.

Share this post


Link to post
Share on other sites

This topic is 4302 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this