Why is this object not garbage collected? [duplicate] - c#

This question already has an answer here:
When is an object subject to garbage collection?
(1 answer)
Closed 7 years ago.
I'm still somewhat confused by C#'s garbage collector. Documentation states that an object is garbage collected when there are no more references pointing to it.
So my question is, why is this object not immediately garbage collected? There is no reference to it. Let's assume that the background class itself creates no references either.
public void StartGame() {
// the background instance is created, but no reference is kept:
new Background("landscape.png", speed);
}

What you are describing is of more resemblance to memory management through reference counting, that checks whether to free the memory when the reference counter is accessed, typically when the referencing object is constructed or destroyed.
Garbage collection is a slightly different concept. Directly freeing the objects in GC-powered environments is typically not allowed or not recommended. Instead, a garbage collector is run occasionally (the runtime decides when to do that), that finds all the no longer referenced objects and frees the memory taken by these. The point is, you should not bother (because you can do nothing about it) when exactly it's going to happen.

The garbage collector is like the garbage truck that drives around your neighborhood. You can't will it to pick up your trash once you put it on the side of the street. You have to wait for it to come on its own terms.
Garbage collectors are theoretically really simple: stop the world, determine what's no longer being used, collect, resume the world. But because this takes time, developers use complex algorithms to decide when the collector kicks in and what it collects, to make your program run as smoothly as possible. Some garbage is usually not a problem that affects your program.
If you are expecting your object to be collected as soon as it goes out of scope, and you're probably using a finalizer to test this. Don't! Instead, implement IDisposable and call the Dispose method yourself, as soon as you're done with it. You can't rely on the garbage collector to collect your object at any time, if ever. That's why the BCL I/O classes all implement IDisposable for flushing streams, closing connections and cleaning up memory.
Or if you want to keep your object around, you need to keep an (indirect) reference to it. The object might just be collected on the next garbage collection cycle.
Well, there's one not recommended way to force the garbage truck to collect your garbage, using GC.Collect:
GC.Collect();
This will temporary stop your program to collect all garbage. However, this might still not clear your Background object while it's living on the stack or some other place. You can't predict where the runtime will put your object and when it will release it, so be sure to at least exit the method that created the object before testing whether it is collected using GC.Collect.

The garbage collector in C# is invoked only at certain moments and is generational. This means that an object that has no reference at the first pass of the GC will be upgraded by 1 generation. The lowest generation is garbage collected way more often than the rest.
You may read this article to understand more: https://msdn.microsoft.com/en-us/library/ee787088%28v=vs.110%29.aspx
You may alternatively call the GC.Collect method, but this is not recommended as .NET is pretty much able to handle its own memory and invoking this method kinda defeats the whole purpose.

Well if you want to check if an object will be garbage collected you can do the following to test in your code if it is eligible for collection by doing the following.
Modify your method a little for testing alternatively you could make reference a field that is set in the StartGame Method.
public void StartGame(out WeakReference reference)
{
reference = new WeakReference(new Background("landscape.png", speed));
}
After your method is called you can do the following to see if it is eligible for collection.
WeakReference reference;
StartGame(out reference);
GC.Collect();
GC.WaitForPendingFinalizers();
if (reference.IsAlive)
{
Console.WriteLine("Background is not eligible for collection");
}
This is only for testing, you shouldn't be calling the Garbage collector otherwise.

Related

Explain if static readonly ConcurrentDictionary<string, AsyncLocal<object>> State leaks memory?

I have an app that seems to accumulate a lot of memory.
One of the suspects is below, and I'm just trying to wrap my head around what it is actually doing. Or, more specifically, how is it cleaned up?
private static readonly ConcurrentDictionary<string, AsyncLocal<object>> State;
Problem context:
The idea is to simulate what OperationContext in WCF would do - provide static access to information about the current call. I am doing this inside a Service Fabric remoting service.
Can someone help me understand the nature of this in terms of what happens to the AsyncLocal<object> once the async call ends? I see it hanging around in memory but can't tell if it is a memory leak, or ig the GC just hasn't reclaimed it yet.
I know the static dictionary stays around, but do the values also, or do I need to be manually clearing those before my current service invocation completes to ensure no memory leak here?
*Edit - Here is some more info as requested by Pavel.
Posting relevant code below, but the whole picture is here.
Github where the general idea came from. They are trying to make headers work in ServiceFabric/.net core like they used to in old WCF.
https://github.com/Expecho/ServiceFabric-Remoting-CustomHeaders
The RemotingContext object is here:
https://github.com/Expecho/ServiceFabric-Remoting-CustomHeaders/blob/master/src/ServiceFabric.Remoting.CustomHeaders/RemotingContext.cs
It's use can be seen here (line 52, among others):
https://github.com/Expecho/ServiceFabric-Remoting-CustomHeaders/blob/master/src/ServiceFabric.Remoting.CustomHeaders/ReliableServices/ExtendedServiceRemotingMessageDispatcher.cs
Here is a code snippet:
public override async Task HandleRequestResponseAsync(IServiceRemotingRequestContext requestContext,
IServiceRemotingRequestMessage requestMessage)
{
var header = requestMessage.GetHeader();
RemotingContext.FromRemotingMessageHeader(header);
//Some other code where the actual service is invoked (and where RemotingContext values can be references for the current call.
return null;
}
The Garbage Collector is a strange beast, so it's worth getting to know about its behaviour. (Note, this is a simplistic view of the GC)
First of all, if just one reference exists to an object, that object is not considered garbage, so it will never be collected.
As your Dictionary is static, it is always going to exist, so anything contained within it will always have a reference to it. If there are no other references to the contained object and you remove it from the Dictionary, it will become unreachable and therefore garbage and will be collected. Making sure there are no references to your objects is the way to ensure they will be collected. It's very easy to forget about some reference you made somewhere, which keeps the object alive.
Secondly, the Garbage Collector doesn't run continuously. It runs when memory resources are getting low and needs to release some memory. This is so that it doesn't hog the CPU to the detriment of the main applications. This means that objects can still be in memory for some time before the next Garbage Collection. This can make memory usage seem high at times, even when it isn't.
Thirdly, the Garbage Collector has "Generations". Generation 0 objects are the newest and most short-lived objects. Generation 0 objects are collected most often, but only when memory is needed.
Generation 1 contains short-lived objects that are on their way to becoming long-lived objects. They are collected less often that Generation 0 objects. In fact, Generation 1 collection only happens if a Generation 0 collection did not release enough memory.
Generation 2 objects are long-lived. These are typically things like static objects which exist for the lifetime of the application. Similarly, these are collected when the Generation 1 collection doesn't release enough memory.
Finally, there is the Large Object heap. Objects that consume a lot of memory take time to move around (part of the garbage collection process involves defragmenting the memory after collection has taken place), so they tend to remain uncollected unless collection didn't release enough memory. Some people refer to this as Generation 3, but they are actually collected in Generation 2 when necessary.

What mechanisms of the C# language are used in order to pass an instance of an object to the `GC.AddMemoryPressure` method?

What mechanisms of the C# language are used in order to pass an instance of an object to the GC.AddMemoryPressure method?
I met the following code sample in the CLR via C# book:
private sealed class BigNativeResource {
private readonly Int32 m_size;
public BigNativeResource(Int32 size) {
m_size = size;
// Make the GC think the object is physically bigger
if (m_size > 0) GC.AddMemoryPressure(m_size);
Console.WriteLine("BigNativeResource create.");
}
~BigNativeResource() {
// Make the GC think the object released more memory
if (m_size > 0) GC.RemoveMemoryPressure(m_size);
Console.WriteLine("BigNativeResource destroy.");
}
}
I can not understand how do we associate an instance of an object with the pressure it adds. I do not see object reference being passed to the GC.AddMemoryPressure. Do we associate the added memory pressure (amp) with an object at all?
Also, I do not see any reasons in calling the GC.RemoveMemoryPressure(m_size);. Literally it should be of no use. Let me explain myself. There are two possibilities: there is an association between the object instance or there is no such association.
In the former case, the GC should now the m_size in order to prioritize and decide when to undertake a collection. So, it definitely should remove the memory pressure by itself (otherwise what would it mean for a GC to remove an object while taking into an account the amp?).
In the later case it is not clear what the use of the adding and removing the amp at all. The GC can only work with the roots which are by definitions instances of classes. I.e. GC only can collect the objects. So, in case there is no association between objects and the amp I see no way how the amp could affect the GC (so I assume there is an association).
I can not understand how do we associate an instance of an object with the pressure it adds.
The instance of the object associates the pressure it adds with a reference to itself by calling AddMemoryPressure. The object already has identity with itself! The code which adds and removes the pressure knows what this is.
I do not see object reference being passed to the GC.AddMemoryPressure.
Correct. There is not necessarily an association between added pressure and any object, and regardless of whether there is or not, the GC does not need to know that information to act appropriately.
Do we associate the added memory pressure (amp) with an object at all?
The GC does not. If your code does, that's the responsibility of your code.
Also, I do not see any reasons in calling the GC.RemoveMemoryPressure(m_size)
That's so that the GC knows that the additional pressure has gone away.
I see no way how the amp could affect the GC
It affects the GC by adding pressure!
I think there is a fundamental misunderstanding of what's going on here.
Adding memory pressure is just telling the GC that there are facts about memory allocation that you know, and that the GC does not know, but are relevant to the action of the GC. There is no requirement that the added memory pressure be associated with any instance of any object or tied to the lifetime of any object.
The code you've posted is a common pattern: an object has additional memory associated with each instance, and it adds a corresponding amount of pressure upon allocation of the additional memory and removes it upon deallocation of the additional memory. But there is no requirement that additional pressure be associated with a specific object or objects. If you added a bunch of unmanaged memory allocations in your static void Main() method, you might decide to add memory pressure corresponding to it, but there is no object associated with that additional pressure.
These methods exist to let GC know about memory usage outside of managed heap. There is no object to pass to these methods because memory is not directly related to any particular managed object. It's responsibility of author of the code to notify GC about change in memory usage correctly.
GC.AddMemoryPressure(Int64)
… the runtime takes into account only the managed memory, and thus underestimates the urgency of scheduling garbage collection.
Extreme example would be you have 32 bit app and GC thinks it can easily allocate almost 2GB of managed (C#) objects. As part of the code you use native interop to allocate 1GB. Without the AddMemoryPressure call GC will still think it's free to wait till you allocate/deallocate a lot of managed objects... but around the time you allocated 1GB of managed objects GC runs into strange state - it should have whole extra GB to play with but there is nothing left so it has to scramble to collect memory at that point. If AddMemoryPressure was properly used GC would had chance to adjust and more aggressively collect earlier in background or at points that allowed shorter/smaller impact.
AddMemoryPressure is used to declare (emphasis here) that you have sensible sized unmanaged data allocated somewhere. This method is a courtesy that the runtime gives to you.
The purpose of the method is to declare under your own responsibility that somewhere you have unmanaged data that is logically bound to some managed object instance. The garbage collector has a simple counter and tracks your request by simply adding the amount you specify to the counter.
The documentation is is clear about that: when the unmanaged memory goes away, you must tell the garbage collector that it has gone away.
You need to use this method to inform the garbage collector that the unmanaged memory is there but could be freed if the associated object is disposed. Then, the garbage collector is able to schedule better its collection tasks.

C# Clearing list before nulling

Today I have seen a piece of code that first seemed odd to me at first glance and made me reconsider. Here is a shortened version of the code:
if(list != null){
list.Clear();
list = null;
}
My thought was, why not replace it simply by:
list = null;
I read a bit and I understand that clearing a list will remove the reference to the objects allowing the GC to do it's thing but will not "resize". The allocated memory for this list stays the same.
On the other side, setting to null would also remove the reference to the list (and thus to its items) also allowing the GC to do it's thing.
So I have been trying to figure out a reason to do it the like the first block. One scenario I thought of is if you have two references to the list. The first block would clear the items in the list so even if the second reference remains, the GC can still clear the memory allocated for the items.
Nonetheless, I feel like there's something weird about this so I would like to know if the scenario I mentioned makes sense?
Also, are there any other scenarios where we would have to Clear() a list right before setting the reference to null?
Finally, if the scenario I mentioned made sense, wouldn't it be better off to just make sure we don't hold multiple references to this list at once and how would we do that (explicitly)?
Edit: I get the difference between Clearing and Nulling the list. I'm mostly curious to know if there is something inside the GC that would make it so that there would be a reason to Clear before Nulling.
The list.Clear() is not necessary in your scenario (where the List is private and only used within the class).
A great intro level link on reachability / live objects is http://levibotelho.com/development/how-does-the-garbage-collector-work :
How does the garbage collector identify garbage?
In Microsoft’s
implementation of the .NET framework the garbage collector determines
if an object is garbage by examining the reference type variables
pointing to it. In the context of the garbage collector, reference
type variables are known as “roots”. Examples of roots include:
A reference on the stack
A reference in a static variable
A reference in another object on the managed heap that is not eligible for garbage
collection
A reference in the form of a local variable in a method
The key bit in this context is A reference in another object on the managed heap that is not eligible for garbage collection. Thus, if the List is eligible to be collected (and the objects within the list aren't referenced elsewhere) then those objects in the List are also eligible to be collected.
In other words, the GC will realise that list and its contents are unreachable in the same pass.
So, is there an instance where list.Clear() would be useful? Yes. It might be useful if you have two references to a single List (e.g. as two fields in two different objects). One of those references may wish to clear the list in a way that the other reference is also impacted - in which list.Clear() is perfect.
This answer started as a comment for Mick, who claims that:
It depends on which version of .NET you are working with. On mobile platforms like Xamarin or mono, you may find that the garbage collector needs this kind of help in order to do its work.
That statement is begging to be fact checked. So, let us see...
.NET
.NET uses a generational mark and sweep garbage collector. You can see the abstract of the algorithm in What happens during a garbage collection
. For summary, it goes over the object graph, and if it cannot reach a object, that one can be erased.
Thus, the garbage collector will correctly identify the items of the list as collectible in the same iteration, regardless of whatever or not you clear the list. There is no need to decouple the objects beforehand.
This means that clearing the list does not help the garbage collector on the regular implementation of .NET.
Note: If there were another reference to the list, then the fact that you cleared the list would be visible.
Mono and Xamarin
Mono
As it turns out, the same is true for Mono.
Xamarin.Android
Also true for Xamarin.Android.
Xamarin.iOS
However, Xamarin.iOS requires additional considerations. In particular, MonoTouch will use wrapped Objective-C objects which are beyond the garbage collector. See Avoid strong circular references under iOS Performance. These objects require different semantics.
Xamarin.iOS will minimize the use of Objetive-C objects by keeping a cache:
C# NSObjects are also created on demand when you invoke a method or a property that returns an NSObject. At this point, the runtime will look into an object cache and determine whether a given Objective-C NSObject has already been surfaced to the managed world or not. If the object has been surfaced, the existing object will be returned, otherwise a constructor that takes an IntPtr as a parameter is invoked to construct the object.
The system keeps these objects alive even there are no references from managed code:
User-subclasses of NSObjects often contain C# state so whenever the Objective-C runtime performs a "retain" operation on one of these objects, the runtime creates a GCHandle that keeps the managed object alive, even if there are no C# visible references to the object. This simplifies bookeeping a lot, since the state will be preserved automatically for you.
Emphasis mine.
Thus, under Xamarin.iOS, if there were a chance that the list might contain wrapped Objetive-C objects, this code would help the garbage collector.
See the question How does memory management works on Xamarin.IOS, Miguel de Icaza explains in his answer that the semantics are to "retain" the object when you take a reference and "release" it when the reference is null.
On the Objetive-C side, "release" does not mean to destroy the object. Objetive-C uses a reference count garbage collector. When we "retain" the object the counter is incremented and when we "release" the counter is decreased. The system destroys the object when the counter reaches zero. See: About Memory Management.
Therefore, Objetive-C is bad at handling circular references (if A references B and B references A, their reference count is not zero, even if they cannot be reached), thus, you should avoid them in Xamarin.iOS. In fact, forgetting to decouple references will lead to leaks in Xamarin.iOS... See: Xamarin iOS memory leaks everywhere.
Others
dotGNU also uses a generational mark and sweep garbage collector.
I also had a look at CrossNet (that compiles IL to C++), it appears they attempted to implement it too. I do not know how good it is.
It depends on which version of .NET you are working with. On mobile platforms like Xamarin or mono, you may find that the garbage collector needs this kind of help in order to do its work. Whereas on desktop platforms the garbage collector implementation may be more elaborate. Each implementation of the CLI out there is going to have it's own implementation of the garbage collector and it is likely to behave differently from one implementation to another.
I can remember 10 years ago working on a Windows Mobile application which had memory issues and this sort of code was the solution. This was probably due to the mobile platform requiring a garbage collector that was more frugal with processing power than the desktop.
Decoupling objects helps simplify the analysis the garbage collector needs to do and helps avoid scenarios where the garbage collector fails to recognise a large graph of objects has actually become disconnected from all the threads in your application. Which results in memory leaks.
Anyone who believes you can't have memory leaks in .NET is an inexperienced .NET developer. On desktop platforms just ensuring Dispose is called on objects which implement them may be enough, however with other implementations you may find it is not.
List.Clear() will decouple the objects in the list from the list and each other.
EDIT: So to be clear I'm not claiming that any particular implementation currently out there is susceptible to memory leaks. And again depending on when this answer is read the robustness of the garbage collector on any implementation of the CLI currently out there could have changed since the time writing this.
Essentially I'm suggesting if you know that your code needs to be cross platform and used across many implementations of the .NET framework, especially implementations of the .NET framework for mobile devices, it could be worth investing time into decoupling objects when they are no longer required. In that case I'd start off by adding decoupling to classes that already implement Dispose, and then if needed look at implementing IDisposable on classes that don't implement IDisposable and ensuring Dispose is called on those classes.
How to tell for sure if it's needed? You need to instrument and monitor the memory usage of your application on each platform it is to be deployed on. Rather than writing lots of superfluous code, I think the best approach is to wait until your monitoring tools indicate you have memory leaks.
As mentioned in the docs:
List.Clear Method (): Count is set to 0, and references to other
objects from elements of the collection are also released.
In your 1st snippet:
if(list != null){
list.Clear();
list = null;
}
If you just set the list to null, it means that you release the reference of your list to the actual object in the memory (so the list itself is remain in the memory) and waiting for the Garbage Collector comes and release its allocated memory.
But the problem is that your list may contain elements that hold a reference to another objects, for example:
list → objectA, objectB, objectC
objectB → objectB1, objectB2
So, after setting the list to null, now list has no reference and it should be collected by Garbage Collector later, but objectB1 and objectB2 has a reference from objectB (still be in the memory) and because of that, Garbage Collector need to analyse the object reference chain. To make it less confusing, this snippet use .Clear() function to remove this confusion.
Clearing the list ensures that if the list is not garbage collected for some reason, then at the very least, the elements it contained can still be disposed of.
As stated in the comments, preventing other references to the list from existing requires careful planning, and clearing the list before nulling it doesn't incur a big enough performance hit to justify trying to avoid doing so.

Minimizing usage of RAM by created objects in runtime in C#

I was wondering what are the best practices when creating objects, performing LINQ in C#. For instance, I realize that when I open up a connection using LINQ I should be putting the model object into a using statement like this:
using(var ctx = new mymodel())
{
}
Now, what about the objects that are EF classes?
They do not implement IDisposable interface thus I can't do something like this when creating an object like this for example:
using(var user = new Users())
{
}
But when an action is called like this:
public ActionResult InsertUser()
{
var user = new Users();
}
I have no clear picture what happens with this object once the insert into the db is finished. Does this object stays allocated in memory or it gets released? If not, what are best practices to release memory once they aren't needed anymore..?
On the other there are static variables as well...
So to sum things up, my questions are:
what are best practices to release memory when creating instances of an object of a class?
Is implementation of IDisposable interface on every class that I have good choice?
When a static variable is created in .NET MVC, what is the best way to release memory taken by this kind of variables?
Same question goes for Session object?
P.S. Guys, I'd really appreciate if all of you that are reading this to post your opinion or post some useful links for some documentations/blog posts so that I can expand my horizons =)
Before you do any performance tweaks I highly recommend to run a memory profiler (for example JetBrains dotMemory, but there are others) and find out the actual source of the problem. Without information from profiler, your optimisations will be like sticking your finger at a sky and shouting "Rainbow!" i.e. useless at best, harmful at worst.
Also after identifying issues with profiler, but before starting changing your code, I recommend reading about how Garbage Collection works in .Net. Here are some references to get you started:
MSDN Garbage Collection
MSDN Garbage Collector Basics and Performance Hints
.Net Garbage Collection in depth
Here are some links to answer your questions:
IDisposable vs Garbage Collector
Static vs Garbage Collector: 1, 2
Session and Garbage Collection
In response to comment.
When you create regular .NET object, .NET runtime knows everything about it, because runtime created it. It knows where in memory it's located, when it is no longer needed and so on. When it is no longer needed - runtime will reclaim its memory. That is managed (by runtime) object. You should not care about memory management for such objects.
Now take for example file handle. When you open file in .NET, it will delegate this operation to OS (for example, Windows). This resource is not managed by runtime, it is managed by OS. So, it is unmanaged resource. So author of .NET library code which works with files (I mean person who created FileStream and similar classes, not uses them) should manually write code to release such resource (close file handle in this case). By convention, author will use IDisposable.Dispose method for code that releases such resources AND ensures that when underlying object (like FileStream) will be collected by GC - unmanaged resources will also be released. So even if you forget to call Dispose - file handle will be closed when GC collects FileStream (not that it will happen not magically but because code author explicitly made it happen like this). But that might happen any time in the future, you don't want to leave file handle open for undetermined time - so you always call Dispose yourself when you are done with IDisposable object.
The same applies to most unmanaged resources. For example, database connection is managed by database, and you don't want to leave database connection open for undetermined time, so you call Dispose and explicitly notify database to close it (and even if you don't call it - author of .NET database code took care to do that when object is collected). EF context wraps database connection, so it also implements IDisposable. If you don't want to leave connection open until context will be collected (and you sure don't want that) - you call Dispose yourself.
Or suppose you write your own code to work with images using ImageMagick. That is C library, so runtime has no idea about its inner workings. You ask library to allocate memory for your image, but .NET cannot reclaim such memory - it does not manage it, it is managed by ImageMagick C library. So you implement IDisposable and tell ImageMagick to release memory in that method.
In the case of var user = new Users(); which does not implement IDisposable, and any object for that matter which is not disposed, will only be guaranteed to exist where there is an active reference to this object; after which it will qualify for disposal the next time the Garbage Collector (GC) tries to free up memory.
In your example above, as soon as this leaves the method InsertUser() it will no longer have any references pointing to it, and therefore will be up for garbage collection.
If however a reference exists, such as in the following code, the object will not be disposed until the reference is cleared, or the containing class is disposed.
private User _insertedUser;
public ActionResult InsertUser()
{
_insertedUser = new Users();
}
The garbage collector fires when the application needs to release some memory. When it fires, it performs several sweeps on objects within memory to tell if any references exist. It first scans every object which has been newly created since the last time the GC was called. Objects which survive this sweep are promoted a generation. If after the sweep more memory is still required, then a 2nd generation sweep is performed. This will scan every object which has survived a single garbage collection to see if it can now be freed. Again if an object survives, it moves up another generation, with 3 generation sweeps in total.
This method helps the GC perform memory management while limiting the high costs involved (newer objects are more likely to be available for release). As GC has a high cost associated to it, it's better if objects are disposed of through user's code to help limit the number of times the GC is called.
tldr;
Dispose objects if they implement IDisposable; an easy way is to
surround it by a 'using'.
If an object cannot be disposed, and it is no longer required, make sure all references to the object is cleared (especially references outside of a method which the object was created).
This enables the GC to release it from memory.

How to dispose heavy set of static variable and collection?

I made a big windows app, and in the program.cs class i am using a lot to static variables
around 20 and some of these are used for big collection of object that i make during the process.
I want to know how should i mange this in the finish of application where and how should i call them dispose. I made GC.Collect but that hangs application and degrade the performance.
And when i dint call GC.Collect it was giving memory lick and hanging for long.
Please let me know how should i manage this static class, static variables. So that performance boost.
I think the way you are using the static variables is completely wrong, you might have misunderstood the concept.
You should mark any field as static only when you know, variables will be accessed through out the life of program i.e. they will be GC'ed only when you terminate the program.
EDIT In C# the memory is automatically managed by the Garbage Collector, the programmer need to know only that when an object which is created is applicable for Garbage Collection. In MSDN ducumentation it is clearly mentioned,
C# employs automatic memory management, which frees developers from manually allocating and freeing the memory occupied by objects. Automatic memory management policies are implemented by a garbage collector. The memory management life cycle of an object is as follows:
When the object is created, memory is allocated for it, the constructor is run, and the object is considered live.
If the object, or any part of it, cannot be accessed by any possible continuation of execution, other than the running of destructors, the object is considered no longer in use, and it becomes eligible for destruction. The C# compiler and the garbage collector may choose to analyze code to determine which references to an object may be used in the future. For instance, if a local variable that is in scope is the only existing reference to an object, but that local variable is never referred to in any possible continuation of execution from the current execution point in the procedure, the garbage collector may (but is not required to) treat the object as no longer in use.
Once the object is eligible for destruction, at some unspecified later time the destructor (Section 10.12) (if any) for the object is run. Unless overridden by explicit calls, the destructor for the object is run once only.
Once the destructor for an object is run, if that object, or any part of it, cannot be accessed by any possible continuation of execution, including the running of destructors, the object is considered inaccessible and the object becomes eligible for collection.
Finally, at some time after the object becomes eligible for collection, the garbage collector frees the memory associated with that object.
In simple words, if an object has no more live references (pointers whose scope has not ended) which are pointing to it, it is eligible for GC.
In your case the static variables are are having the scope which extends across the entire run of the program and once you remove those references either by assigning those to new objects or assigning them to null then old objects will be applicable for GC if no other live references pointing to old objects are alive.
You have IDisposable objects (Forms and Controls, I'm guessing) and need to dispose them at some appropriate time. The best way to solve this is going to be to create an object model appropriate to your domain, and have it manage them. I don't know about your domain, so it's hard to give details about that.
To make it more convenient, you could group these variables together in one class. Instead of holding these variables in the Program class, put them in another class that Program references, and make that class IDisposable. Something like this:
public class Resources : IDisposable
{
public MyForm MyForm {get;set;}
public MyControl MyControl {get;set;}
//etc...
public void Dispose()
{
if(MyForm != null)
MyForm.Dispose()
if(MyControl != null)
MyControl.Dispose()
//etc..
}
}
If the problem with this is that you want to avoid bugs around accessing Disposed objects, don't make them static! If they are not static, you can control what object bind to them more clearly, and make sure those object stop holding a reference to them when they are disposed.

Categories