I read that GC (Garbage Collectors) moves data in Heap for performance reasons, which I don't quite understand why since it is random access memory, maybe for better sequential access but I wonder if references in Stack get updated when such a move occurs in Heap. But maybe the offset address remains the same but other parts of data get moved by Garbage Collectors, I am not sure though.
I think this question pertains to implementation detail since not all garbage collectors may perform such optimization or they may do it but not update references (if it is a common practice among garbage collector implementations). But I would like to get some overall answer specific to CLR (Common Language Runtime) garbage collectors though.
And also I was reading Eric Lippert's "References are not addresses" article here, and the following paragraph confused me little bit:
If you think of a reference is actually being an opaque GC handle then
it becomes clear that to find the address associated with the handle
you have to somehow "fix" the object. You have to tell the GC "until
further notice, the object with this handle must not be moved in
memory, because someone might have an interior pointer to it". (There
are various ways to do that which are beyond the scope of this
screed.)
It sounds like for reference types, we don't want data to be moved. Then what else we store in the heap, which we can move around for performance optimization? Maybe type information we store there? By the way, in case you wonder what that article is about, then Eric Lippert is comparing references to pointers little bit and try to explain how it may be wrong to say that references are just addresses even though it is how C# implements it.
And also, if any of my assumptions above is wrong, please correct me.
Yes, references get updated during a garbage collection. Necessarily so, objects are moved when the heap is compacted. Compacting serves two major purposes:
it makes programs more efficient by using the processor's data caches more efficiently. That is a very, very big deal on modern processors, RAM is exceedingly slow compared to the execution engine, a fat two orders of magnitude. The processor can be stalled for hundreds of instructions when it has to wait for RAM to supply a variable value.
it solves the fragmentation problem that heaps suffer from. Fragmentation occurs when a small object is released that is surrounded by live objects. A hole that cannot be used for anything else but an object of equal or smaller size. Bad for memory usage efficiency and processor efficiency. Note how the LOH, the Large Object Heap in .NET, does not get compacted and therefore suffers from this fragmentation problem. Many questions about that at SO.
In spite of Eric's didactic, an object reference really is just an address. A pointer, exactly the same kind you'd use in a C or C++ program. Very efficient, necessarily so. And all the GC has to do after moving an object is update the address stored in that pointer to the moved object. The CLR also permits allocating handles to objects, extra references. Exposed as the GCHandle type in .NET, but only necessary if the GC needs help determining if an object should stay alive or should not be moved. Only relevant if you interop with unmanaged code.
What is not so simple is finding that pointer back. The CLR is heavily invested in ensuring that can be done reliably and efficiently. Such pointers can be stored in many different places. The easier ones to find back are object references stored in a field of an object, a static variable or a GCHandle. The hard ones are pointers stored on the processor stack or a CPU register. Happens for method arguments and local variables for example.
One guarantee that the CLR needs to provide to make that happen is that the GC can always reliably walk the stack of a thread. So it can find local variables back that are stored in a stack frame. Then it needs to know where to look in such a stack frame, that's the job of the JIT compiler. When it compiles a method, it doesn't just generate the machine code for the method, it also builds a table that describes where those pointers are stored. You'll find more details about that in this post.
Looking at C++\CLI In Action, there's a section about interior pointers vs pinning pointers:
C++/CLI provides two kinds of pointers that work around this problem.
The first kind is called an interior pointer, which is updated by the
runtime to reflect the new location of the object that's pointed to
every time the object is relocated. The physical address pointed to by
the interior pointer never remains the same, but it always points to
the same object. The other kind is called a pinning pointer, which
prevents the GC from relocating the object; in other words, it pins
the object to a specific physical location in the CLR heap. With some
restrictions, conversions are possible between interior, pinning, and
native pointers.
From that, you can conclude that reference types do move in the heap and their addresses do change. After the Mark and Sweep phase, the objects get compacted inside the heap, thus actually moving to new addresses. The CLR is responsible to keep track of the actual storage location and update those interior pointers using an internal table, making sure that when accessed, it still points to the valid location of the object.
There's an example taken from here:
ref struct CData
{
int age;
};
int main()
{
for(int i=0; i<100000; i++) // ((1))
gcnew CData();
CData^ d = gcnew CData();
d->age = 100;
interior_ptr<int> pint = &d->age; // ((2))
printf("%p %d\r\n",pint,*pint);
for(int i=0; i<100000; i++) // ((3))
gcnew CData();
printf("%p %d\r\n",pint,*pint); // ((4))
return 0;
}
Which is explained:
In the sample code, you create 100,000 orphan CData objects ((1)) so
that you can fill up a good portion of the CLR heap. You then create a
CData object that's stored in a variable and ((2)) an interior pointer
to the int member age of this CData object. You then print out the
pointer address as well as the int value that is pointed to. Now,
((3)) you create another 100,000 orphan CData objects; somewhere along
the line, a garbage-collection cycle occurs (the orphan objects
created earlier ((1)) get collected because they aren't referenced
anywhere). Note that you don't use a GC::Collect call because that's
not guaranteed to force a garbage-collection cycle. As you've already
seen in the discussion of the garbage-collection algorithm in the
previous chapter, the GC frees up space by removing the orphan objects
so that it can do further allocations. At the end of the code (by
which time a garbage collection has occurred), you again ((4)) print
out the pointer address and the value of age. This is the output I got
on my machine (note that the addresses will vary from machine to
machine, so your output values won't be the same):
012CB4C8 100
012A13D0 100
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
CLR garbage collector actively goes through all objects that have been created and works out if they are being used. But, how does garbage collector decide which object are to be killed and which are in use?
I understand the concept of assigning a null value to object will suffice. But, what if I write only
string obj = new string(new char[] {'a'});
and not null assignment lineobj = null;.
How will garbage collector determine when to clean it?
The CLR Garbage Collector is (at its core) a so-called tracing GC. (The other "big" class of garbage collectors are so-called reference-counting GCs.)
Tracing GCs work by, well recursively "tracing" the set of reachable objects from a set of objects that are already known to be reachable. Here's how that works:
Assume that we already have a set of objects that we know are reachable. For every object in that set, follow all the references (e.g. fields, and also internal pointers such as the class pointer etc.). Those objects are also reachable. Repeat until you have visited all objects at least once. Now you know all reachable objects. (We could say that we have computed the transitive closure with respect to reachability.) All objects that you haven't visited are not reachable and thus eligible for garbage collection.
Now, we just have to figure out how to start this algorithm, i.e. how do get the first set of objects that are known to be reachable. Well, every language usually has a set of objects that are known to be always reachable. This set from which we are starting our trace from, is called the root set. It includes things like:
globals
pointers in CPU registers
objects referenced by local variables on the stack
objects on the stack
Thread-Local Storage
unsafe memory
native memory
VM-internal data structures
the root namespace
…
That's it.
There are, of course, many variations of this theme. The most simple implementation of this tracing idea is called mark-sweep. It has two phases, mark and sweep (duh!) The mark phase is the tracing phase, you trace the reachable objects, and then you set a bit in the object header which says "yep, reachable". In the sweep phase, you collect all objects which don't have the bit set and reset the bit to false in the other objects.
A slight improvement of this scheme is to keep a separate marking table. For one, you don't have to write all over the entire RAM just to set those marking bits (which throws all data out of the cache, for example, and also triggers a copy-on-write if the memory is shared with another process). And secondly, you don't have to visit the reachable objects to reset the marking bit, you can just throw away the marking table after you're done.
The biggest dis-advantage of this scheme is that it leads to memory fragmentation. The biggest advantage is that objects don't move around in memory, which means for example that you can hand out pointers to objects without fear that those pointers may become invalid.
Another, very simple scheme, is Henry Baker's semi-space copying collector. It is called "semi-space" because it always uses at most 50% of the allocated memory. It is also a tracing collector, but it is a copying collector instead of mark-sweep. Instead of marking the objects when visiting them, it copies them over to the empty half of the memory. Afterwards, the old half can be simply freed in constant time.
The advantage is that everytime you copy the objects, they will be neatly tightly packed in memory without holes, so there is no fragmentation. But, they move around in memory, so you cannot just hand out pointers to those objects.
Note: the CLR's Garbage Collectors (it actually has two of them!) are much more complex and sophisticated than those two schemes I presented. They are, however, both tracing GCs.
The second big class of collectors are reference-counting collectors. Instead of tracing references only when a collection occurs, they count references, everytime a reference is created or destroyed. So, when you assign an object to a local variable, or a field, or pass it as an argument, …, the system increments a reference counter in the object header, and everytime you assign a different object to a local variable, or the local variable goes out of scope, or the object that the field belongs to gets GCd, …, the reference is decremented. If the reference count hits 0, there are no more references, and the object is eligible for garbage collection.
The big advantage of this scheme is that you always know exactly when an object becomes unreachable. The big disadvantage is that you can get disconnected cycles whose reference count(s) will never be 0. If you have a reference from A → B, from B → C, from C → A, and from D → B, then A's reference count is 1, B's reference count is 2, C's reference count is 1. If you now remove the reference from D, B's reference count drops to 1, and there is no reference from the rest of the system to either A or B or C, so they are all not reachable, but their reference count will never drop to 0, so they will never be collected.
A third big idea in GC is the Generational Hypothesis:
Objects die young
Older objects don't reference younger objects
As it turns out, for typical systems, this is true for almost all objects. Which means, it makes sense to treat objects differently depending on their age. A generational GC divides the objects into different generations, and has different garbage collection and memory allocation strategies for each. (Let's leave it at that.)
For more information about garbage collection in general, you should read The Garbage Collection Handbook – The art of automatic memory management by Richard Jones, Antony Hosking, Eliot Moss.
What happen at background(in case of memory) when I declare variable and then create object for that variable . Is reference variable store anywhere and in which format and how this variable points to the memory on heap. Please clarify below doubts in the comments.
For example
ClassA instance; // Where this variable store and how much memory occupies
instance=new ClassA(); //How instance variable points to memory
EDIT
What will effect on my program memory if my program contains so many unused variable.
The instance variable is just a pointer at runtime, it points to the object allocated in the GC heap. The variable can live anywhere, stack, CPU register, inside another object that's on the heap or in the loader heap if it is static.
The big deal about the garbage collector is that it is capable of finding this pointer during a garbage collection. And can thus see that the object is still referenced and can adjust the pointer value when it compacts the heap. That's fairly straight-forward when the reference is static on inside another object. Harder when it is on the stack or a register, the jitter provides sufficient info to let the GC find it.
A reference variable is stored inline. If it's a local variable, it's allocated on the stack, if it's a member of a class it's allocated as part of the object on the heap.
An instance of a class is always allocated on the heap.
A reference is just a pointer, but what's special is that the garbage collector is aware of the reference. So, a reference uses the amount of space that a pointer uses. In a 32 bit process it uses 4 bytes, in a 64 bit process it uses 8 bytes.
The storage location of a local variable for the reference itself is platform dependent (jitters can choose where they want to store it.) Typically it will be in memory on the call stack for the method defining the local or in a CPU register. The size is also platform dependent, but is generally 4 bytes for 32-bit architecture and 8 bytes for 64-bit architecture.
The reference may or may not 'point' to the heap. It's better to think of it as an opaque reference identifier which can be used for accessing the object. The underlying pointer can change at runtime.
Regarding unused variables, the optimizing compiler will often eliminate any unused local variables entirely, so it has no impact at all on runtime performance. Also, the type of overhead you're talking about for storing a reference is miniscule for modern platforms.
If you need answers about this then I would recommend you get your hands on "CLR via C#", it is a book about how the CLR functions and it includes lots of information about this.
To answer your question, there are many things that you need to think about to answer this question.
For instance you need to store the instructions for each method in the class. When the class first loads this will effectively be a pointer to the .Net IL instructions. When the method is first needed by the application, it will be JIT compiled to actual instructions for the processor and this will be stored in memory.
Then you have static storage for class fields that will be stored only once per class.
Each class in .Net that is instantiated requires storage for various reasons, but not limited to things like inheritance, garbage collection, layout. Then you have storage for the various references that you may keep to the object which will itself take storage.
If memory is truly critical to what you are doing, then C# may not be the best choice for your application. Otherwise, just enjoy the benefits in productivity you will gain from using .NET and accept that this ease of use comes with a price of memory usage and less performance (in some cases) from a C/C++ app.
I have just started with .NET Framework with C# as my language. I somewhat understand the concept of GC in Java, and had a revisit to the same concept in .NET today.
In C#, the value types are put onto the stack(same as the case with Java,where local variables are put onto the stack). But in C#, even struct is included in value types. So, even structs are placed onto the stack. In a worst case scenario, where there are many method calls, and the stack is populated heavily with many methods, and each method has many local value types, and many structs that themselves have many local value types, will the Garbage Collector ever affect the stack? From what I researched(and partly what I was taught about), I understand that it won't do so. Primarily because manipulating stack content will involve a lot of overhead, and besides, GC only consults stack to lookup for references - and nothing more than that.
Just to add another question related on the same topic : Forcing a call to GC(like System.gc() in Java, not sure about the C# equivalent), doesn't ensure that the GC routine is called then and there. So where should I place such a call - where I expect that I need the GC to run, or any random place as there is no guarantee that my call would immediately trigger the GC? Or should I just leave the stuff to the Runtime Environment and not bother about it?
Note: I added the Java tag because I'm trying to link concepts from there. I understand that the internal functioning of GC in the two separate Runtime Environments will definitely be different, but I guess the underlying concept would be the same.
No garbage collect does not affect objects on the java stack.
GC only affects objects in the jvm's heap. The java GC process is multi-tiered and can be very complex, and worth reading up on. Check out a site like: http://javarevisited.blogspot.com/2011/04/garbage-collection-in-java.html to get a good grasp on how it operates.
As far as forcing the system's GC that is a bad idea. The jvm will have a better idea that you when a GC needs to run. If you are attempting to allocate a big object, the jvm will ensure the space is there for you without you needing to tell it to run the GC.
EDIT
My bad, you are more concerned about C# than java. The same principals of memory management apply, stack is unaffected, don't explicitly run a GC, etc. C# is designed to operate in a similar manor to java. http://msdn.microsoft.com/en-us/library/ms973837.aspx
Stacks don't need the assistance of a garbage collector; because, as you move out of stack frames (the scope of the current execution within the stack), the entire frame, including contents, is freed (and overwritten) as you create a new stack frame.
function foo(int a, int b) {
int i;
doStuff();
}
creates a stack frame (rough visualization)
---- Frame Start ----
(value for parameter a)
(value for parameter b)
(other items needed for tracking execution)
(extra stack frame space
(value for stack allocated i)
)
---- End of Frame ----
When entering a function, stack allocated variables are allocated as the frame is allocated, when exiting the frame, the entire frame is discarded, deallocating the memory for frame allocated variables.
Keep in mind that Java typically allocates object references and stack local primitives on the stack, not whole objects. Only a few recent optimizations permit in-stack allocation of objects not reachable outside the frame; which has such conditions on it that it is not considered something you can count on.
That said, references in the stack frame typically point to the heap, which is garbage collected normally.
If you read this for .NET, it only works on the managed heap:
http://msdn.microsoft.com/en-us/library/ee787088.aspx
MSDN seems to be a treasure trove of information, here is the parent topic on the GC in the CLR:
http://msdn.microsoft.com/en-us/library/0xy59wtx
.NET garbage collection is explained in depth in Garbage Collection on MSDN. The Garbage Collector tracks memory only in the Managed Heap.
No. AFAIK GC doesn't effect stack. It effects only HEAP memory. Stack frame will be created upon method calls and will be removed on method exit.
EDIT
This MSDN article explains how GC works in .NET framework.
I'm converting a C# project to C++ and have a question about deleting objects after use. In C# the GC of course takes care of deleting objects, but in C++ it has to be done explicitly using the delete keyword.
My question is, is it ok to just follow each object's usage throughout a method and then delete it as soon as it goes out of scope (ie method end/re-assignment)?
I know though that the GC waits for a certain size of garbage (~1MB) before deleting; does it do this because there is an overhead when using delete?
As this is a game I am creating there will potentially be lots of objects being created and deleted every second, so would it be better to keep track of pointers that go out of scope, and once that size reachs 1MB to then delete the pointers?
(as a side note: later when the game is optimised, objects will be loaded once at startup so there is not much to delete during gameplay)
Your problem is that you are using pointers in C++.
This is a fundamental problem that you must fix, then all your problems go away. As chance would have it, I got so fed up with this general trend that I created a set of presentation slides on this issue. – (CC BY, so feel free to use them).
Have a look at the slides. While they are certainly not entirely serious, the fundamental message is still true: Don’t use pointers. But more accurately, the message should read: Don’t use delete.
In your particular situation you might find yourself with a lot of long-lived small objects. This is indeed a situation which a modern GC handles quite well, and which reference-counting smart pointers (shared_ptr) handle less efficiently. If (and only if!) this becomes a performance problem, consider switching to a small object allocator library.
You should be using RAII as much as possible in C++ so you do not have to explicitly deleteanything anytime.
Once you use RAII through smart pointers and your own resource managing classes every dynamic allocation you make will exist only till there are any possible references to it, You do not have to manage any resources explicitly.
Memory management in C# and C++ is completely different. You shouldn't try to mimic the behavior of .NET's GC in C++. In .NET allocating memory is super fast (basically moving a pointer) whereas freeing it is the heavy task. In C++ allocating memory isn't that lightweight for several reasons, mainly because a large enough chunk of memory has to be found. When memory chunks of different sizes are allocated and freed many times during the execution of the program the heap can get fragmented, containing many small "holes" of free memory. In .NET this won't happen because the GC will compact the heap. Freeing memory in C++ is quite fast, though.
Best practices in .NET don't necessarily work in C++. For example, pooling and reusing objects in .NET isn't recommended most of the time, because the objects get promoted to higher generations by the GC. The GC works best for short lived objects. On the other hand, pooling objects in C++ can be very useful to avoid heap fragmentation. Also, allocating a larger chunk of memory and using placement new can work great for many smaller objects that need to be allocated and freed frequently, as it can occur in games. Read up on general memory management techniques in C++ such as RAII or placement new.
Also, I'd recommend getting the books "Effective C++" and "More effective C++".
Well, the simplest solution might be to just use garbage collection in
C++. The Boehm collector works well, for example. Still, there are
pros and cons (but porting code originally written in C# would be a
likely candidate for a case where the pros largely outweigh the cons.)
Otherwise, if you convert the code to idiomatic C++, there shouldn't be
that many dynamically allocated objects to worry about. Unlike C#, C++
has value semantics by default, and most of your short lived objects
should be simply local variables, possibly copied if they are returned,
but not allocated dynamically. In C++, dynamic allocation is normally
only used for entity objects, whose lifetime depends on external events;
e.g. a Monster is created at some random time, with a probability
depending on the game state, and is deleted at some later time, in
reaction to events which change the game state. In this case, you
delete the object when the monster ceases to be part of the game. In
C#, you probably have a dispose function, or something similar, for
such objects, since they typically have concrete actions which must be
carried out when they cease to exist—things like deregistering as
an Observer, if that's one of the patterns you're using. In C++, this
sort of thing is typically handled by the destructor, and instead of
calling dispose, you call delete the object.
Substituting a shared_ptr in every instance that you use a reference in C# would get you the closest approximation at probably the lowest effort input when converting the code.
However you specifically mention following an objects use through a method and deleteing at the end - a better approach is not to new up the object at all but simply instantiate it inline/on the stack. In fact if you take this approach even for returned objects with the new copy semantics being introduced this becomes an efficient way to deal with returned objects also - so there is no need to use pointers in almost every scenario.
There are a lot more things to take into considerations when deallocating objects than just calling delete whenever it goes out of scope. You have to make sure that you only call delete once and only call it once all pointers to that object have gone out of scope. The garbage collector in .NET handles all of that for you.
The construct that is mostly corresponding to that in C++ is tr1::shared_ptr<> which keeps a reference counter to the object and deallocates when it drops to zero. A first approach to get things running would be to make all C# references in to C++ tr1::shared_ptr<>. Then you can go into those places where it is a performance bottleneck (only after you've verified with a profile that it is an actual bottleneck) and change to more efficient memory handling.
GC feature of c++ has been discussed a lot in SO.
Try Reading through this!!
Garbage Collection in C++
Ok so I understand about the stack and the heap (values live on the Stack, references on the Heap).
When I declare a new instance of a Class, this lives on the heap, with a reference to this point in memory on the stack. I also know that C# does it's own Garbage Collection (ie. It determines when an instanciated class is no longer in use and reclaims the memory).
I have 2 questions:
Is my understanding of Garbage Collection correct?
Can I do my own? If so is there any real benefit to doing this myself or should I just leave it.
I ask because I have a method in a For loop. Every time I go through a loop, I create a new instance of my Class. In my head I visualise all of these classes lying around in a heap, not doing anything but taking up memory and I want to get rid of them as quickly as I can to keep things neat and tidy!
Am I understanding this correctly or am I missing something?
Ok so I understand about the stack and the heap (values live on the Stack, references on the Heap
I don't think you understand about the stack and the heap. If values live on the stack then where does an array of integers live? Integers are values. Are you telling me that an array of integers keeps its integers on the stack? When you return an array of integers from a method, say, with ten thousand integers in it, are you telling me that those ten thousand integers are copied onto the stack?
Values live on the stack when they live on the stack, and live on the heap when they live on the heap. The idea that the type of a thing has to do with the lifetime of its storage is nonsense. Storage locations that are short lived go on the stack; storage locations that are long lived go on the heap, and that is independent of their type. A long-lived int has to go on the heap, same as a long-lived instance of a class.
When I declare a new instance of a Class, this lives on the heap, with a reference to this point in memory on the stack.
Why does the reference have to go on the stack? Again, the lifetime of the storage of the reference has nothing to do with its type. If the storage of the reference is long-lived then the reference goes on the heap.
I also know that C# does it's own Garbage Collection (ie. It determines when an instanciated class is no longer in use and reclaims the memory).
The C# language does not do so; the CLR does so.
Is my understanding of Garbage Collection correct?
You seem to believe a lot of lies about the stack and the heap, so odds are good no, it's not.
Can I do my own?
Not in C#, no.
I ask because I have a method in a For loop. Every time I go through a loop, I create a new instance of my Class. In my head I visualise all of these classes lying around in a heap, not doing anything but taking up memory and I want to get rid of them as quickly as I can to keep things neat and tidy!
The whole point of garbage collection is to free you from worrying about tidying up. That's why its called "automatic garbage collection". It tidies for you.
If you are worried that your loops are creating collection pressure, and you wish to avoid collection pressure for performance reasons then I advise that you pursue a pooling strategy. It would be wise to start with an explicit pooling strategy; that is:
while(whatever)
{
Frob f = FrobPool.FetchFromPool();
f.Blah();
FrobPool.ReturnToPool(f);
}
rather than attempting to do automatic pooling using a resurrecting finalizer. I advise against both finalizers and object resurrection in general unless you are an expert on finalization semantics.
The pool of course allocates a new Frob if there is not one in the pool. If there is one in the pool, then it hands it out and removes it from the pool until it is put back in. (If you forget to put a Frob back in the pool, the GC will get to it eventually.) By pursuing a pooling strategy you cause the GC to eventually move all the Frobs to the generation 2 heap, instead of creating lots of collection pressure in the generation 0 heap. The collection pressure then disappears because no new Frobs are allocated. If something else is producing collection pressure, the Frobs are all safely in the gen 2 heap where they are rarely visited.
This of course is the exact opposite of the strategy you described; the whole point of the pooling strategy is to cause objects to hang around forever. Objects hanging around forever is a good thing if you're going to use them.
Of course, do not make these sorts of changes before you know via profiling that you have a performance problem due to collection pressure! It is rare to have such a problem on the desktop CLR; it is rather more common on the compact CLR.
More generally, if you are the kind of person who feels uncomfortable having a memory manager clean up for you on its schedule, then C# is not the right language for you. Consider C instead.
values live on the Stack, references on the Heap
This is an implementation detail. There is nothing to stop a .NET Framework from storing both on the stack.
I also know that C# does it's own Garbage Collection
C# has nothing to do with this. This is a service provided by the CLR. VB.NET, F#, etc all still have garbage collection.
The CLR will remove an object from memory if it has no strong roots. For example, when your class instance goes out of scope in your for loop. There will be a few lying around, but they will get collected eventually, either by garbage collection or the program terminating.
Can I do my own? If so is there any real benefit to doing this myself or should I just leave it?
You can use GC.Collect to force a collection. You should not do it because it is an expensive operation. More expensive than letting a few objects occupy memory a little bit longer than they are absolutely needed. The GC is incredibly good at what it does on its own. You will also force short lived objects to promote to generations they wouldn't get normally.
First off, to Erics seminal post about The truth about value types
Secondly on Garbage collection, the collector knows far more about your running program than you do, don't try to second guess it unless you're in the incredibly unlikely situation that you have a memory leak.
So to your second question, no don't try to "help" the GC.
I'll find a post to this effect on the CG and update this answer.
Can I do my own? If so is there any real benefit to doing this myself or should I just leave it.
Yes you can with GC.Collect but you shouldn't. The GC is optimized for variables that are short lived, ones in a method, and variables that are long lived, ones that generally stick around for the life time of the application.
Variables that are in-between aren't as common and aren't really optimum for the GC.
By forcing a GC.Collect you're more likely to cause variables in scope to be in forced into that in-between state which is the opposite from you are trying to accomplish.
Also from the MSDN article Writing High-Performance Managed Applications : A Primer
The GC is self-tuning and will adjust itself according to applications
memory requirements. In most cases programmatically invoking a GC will
hinder that tuning. "Helping" the GC by calling GC.Collect will more
than likely not improve your applications performance
Your understanding of Garbage Collection is good enough. Essentially, an unreferenced instance is deemed as being out-of-scope and no longer needed. Having determined this, the collector will remove an unreferenced object at some future point.
There's no way to force the Garbage Collector to collect just a specific instance. You can ask it to do its normal "collect everything possible" operation GC.Collect(), but you shouldn't.; the garbage-collector is efficient and effective if you just leave it to its own devices.
In particular it excels at collecting objects which have a short lifespan, just like those that are created as temporary objects. You shouldn't have to worry about creating loads of objects in a loop, unless they have a long lifespan that prevents immediate collection.
Please see this related question with regard to the Stack and Heap.
In your specific scenario, agreed, if you new up objects in a for-loop then you're going to have sub-optimal performance. Are the objects stored (or otherwise used) within the loop, or are they discarded? If the latter, can you optimize this by newing up one object outside the loop and re-using it?
With regard to can you implement your own GC, there is no explicit delete keyword in C#, you have to leave it to the CLR. You can however give it hints such as when to collect, or what to ignore during collection, however I'd leave that unless absolutely necessary.
Best regards,
Read the following article by Microsoft to get a level of knowledge about Garbage Collection in C#. I'm sure it'll help anyone who need information regarding this matter.
Memory Management and Garbage Collection in the .NET Framework
If you are interested in performance of some areas in your code when writing C#, you can write unsafe code. You will have a plus of performance, and also, in your fixed block, the garbage collector most likely will not occur.
Garbage collection is basically reference tracking. I can't think of any good reason why you would want to change it. Are you having some sort of problem where you find that memory isn't being freed? Or maybe you are looking for the dispose pattern
Edit:
Replaced "reference counting" with "reference tracking" to not be confused with the Increment/Decrement Counter on object Reference/Dereference (eg from Python).
I thought it was pretty common to refer to the object graph generation as "Counting" like in this answer:
Why no Reference Counting + Garbage Collection in C#?
But I will not pick up the glove of (the) Eric Lippert :)