C # Destructor not working properly

See code below. I expect it to print either 10, because I explicitly called the garbage collector. But I always get either 0 or 20 as output. Why is this?

void Main() { Panda[] forest_panda = new Panda[10]; for(int i=0; i<forest_panda.GetLength(0);i++) { forest_panda[i]=new Panda("P1"); } for(int i=0; i<forest_panda.GetLength(0);i++) { forest_panda[i]=new Panda("P1"); } System.GC.Collect(); Console.WriteLine("Total Pandas created is {0}",Panda.population); } class Panda { public static int population=0; public string name; public Panda(string name) { this.name = name; population = population + 1; } ~Panda() { population = population - 1; } } 

Note that the class for Main is automatically created by LINQPad (the editor that comes with the book "C # 4.0 in a nutshell"). I am new to C #.

+5
source share
8 answers

You have not started garbage collection. From the GC.Collect () docs:

Use this method to try to recover all memory inaccessible. However, the Collect method does not guarantee that all inaccessible memory is repaired.

All objects, no matter how long they have been in memory, are considered to be collected; However, objects referenced by managed code are not collected. Use this method to make the system try to return the maximum amount of available memory.

The garabak collector is highly optimized and "solves" everything by itself when it really does the garbage collection, and then calls the finalizers. In addition, all this is done asynchronously. That is why Finalizer is called non-deterministic cleaning. You will never be when the cleaning happens.

You now have two options. You can either call GC.WaitForPendingFinalizers () , which will stop the current thread until all finalized objects are complete. Or call this new overload: System.GC.Collect (int generation, System.GCCollectionMode mode) with GCCollectionMode.Forced It was introduced in .NET. 3.5.

Just keep in mind what is usually not needed and more importantly: a bad idea to call the garbage collector manually. The finalizer implementation is also necessary only in rare cases. Calling the garbage collector will slow down the execution time. Implementing finalizers will additionally slow down execution. The garage collector puts all objects that implement the finalizer in the finalization queue when they are ready to assemble the garage. Handling this line is expensive. To make matters worse when the finalizer starts, it is not guaranteed that the members you are trying to access are still alive. It is very possible that they have already been assembled. This is why you should only use the finalizer when you have unmanaged resources that need to be cleaned up.

All this in your example is not necessary. What you need is IDisposable for deterministic cleaning.

+7
source

Here you can notice a few things:

First of all, the GC behaves differently between release builds and debugging. Typically, in release mode, objects can be restored earlier than in debug mode.

As Tim points out, calling GC.Collect does not call finalizers. If you want to wait for the finalizers to finish calling the GC.WaitForPendingFinalizers.

Finalizers are triggered by a dedicated thread, so you actually change state from two different threads without any synchronization. Although this may not be a problem in this particular case, doing it is not very good. But before you go and add synchronization to your finalizer, keep in mind that a locked finalizer means that there will be no more finalizers, and therefore the memory for these objects will not be restored.

+6
source

Try adding System.GC.WaitForPendingFinalizers after garbage collection.
http://www.developer.com/net/csharp/article.php/3343191/C-Tip-Forcing-Garbage-Collection-in-NET.htm

+3
source

You create twenty objects, so then the value will be 20. Explicitly, calling System.GC.Collect () does not actually guarantee a call to the destructor. Therefore, if it was called, all 20 objects may or may not have been destroyed.

This explains what is actually happening.

Bad practice of creating a destructor or calling GC.Collect explicitly.

If an object needs to perform a cleanup, it must implement IDisposable

+2
source

In .NET, the lifetime of an object is not deterministic and does not behave as you would expect from a C ++ constructor / destructor. In fact, .NET objects do not technically have destructors. The finalizer is characterized in that it is expected to clear the unmanaged resources used by the object during its lifetime.

To have a deterministic way of freeing resources used by your object, you implement the IDisposable interface. IDisposable is not perfect, although it still requires the calling code to correctly delete the object when it was done, and it is difficult for it to handle random multiple Dispose calls. However, the syntax in C # makes it generally very simple.

 class Panda : IDisposable { public static int population = 0; public string _name; public Panda( string name ) { if( name == null ) throw new ArgumentNullException( name ); _name = name; population++; } protected virtual void Dispose( bool disposing ) { if( disposing && name != null ) { population--; name = null; } } public void Dispose() { Dispose( true ); GC.SuppressFinalize( this ); } ~Panda(){ Dispose( false ); } } 

Then, to use the class:

 using( var panda = new Panda( "Cute & Cuddly" ) ) { // Do something with the panda } // panda.Dispose() called automatically 
+1
source

Using destructors (aka finalizers) is not really a good way to do things in C #. There is no guarantee that the finalizer will work even if you explicitly call the garbage collector. You should also not try to force garbage collection, because it is likely to adversely affect your application as a whole.

Instead, if you need to explicitly free resources belonging to an object, you must implement the IDisposable interface and put your logic cleanup inside the Dispose () method. Conversely, when you use an object that implements IDisposable, you should always take care of calling the Dispose () method when you are done with it. For this purpose, C # provides a "using" statement.

Many classes that perform I / O (for example, threads) implement IDisposable. The following is an example of using FileStream to read a text file. Pay attention to the using statement to ensure that the FileStream will be deleted when we are done with it:

 using (FileStream fs = File.OpenRead("C:\\temp\\myfile.txt")) { // Read a text file 1024 bytes at a time and write it to the console byte[] b = new byte[1024]; while (fs.Read(b, 0, b.Length) > 0) { Console.WriteLine(Encoding.UTF8.GetString(b)); } } // Dispose() is called automatically here 

The above code is equivalent to this:

 FileStream fs = File.OpenRead("C:\\temp\\myfile.txt")) try { // Read a text file 1024 bytes at a time and write it to the console byte[] b = new byte[1024]; while (fs.Read(b, 0, b.Length) > 0) { Console.WriteLine(Encoding.UTF8.GetString(b)); } } finally { fs.Dispose(); } 
+1
source

It’s best to use the fix pattern . Here is the full implementation of your work.

Remember that you need to invoke Dispose on your own, as in the code below.

  public static void Main() { Panda[] forest_panda = new Panda[10]; for (int i = 0; i < forest_panda.GetLength(0); i++) forest_panda[i] = new Panda("P1"); // Dispose the pandas by your own foreach (var panda in forest_panda) panda.Dispose(); for (int i = 0; i < forest_panda.GetLength(0); i++) forest_panda[i] = new Panda("P1"); // Dispose the pandas by your own foreach (var panda in forest_panda) panda.Dispose(); Console.WriteLine("Total Pandas created is {0}", Panda.population); } class Panda : IDisposable { public static int population = 0; public string name; public Panda(string name) { this.name = name; population = population + 1; } ~Panda() { Dispose(false); } /// <summary> /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// </summary> /// <filterpriority>2</filterpriority> public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (disposing) { population = population - 1; } } } 
+1
source

This is called for me in the release build. However, it behaved differently in debugging.

0
source

Source: https://habr.com/ru/post/920742/


All Articles