-4

How can I print the number of instances after I deleted a constructor? Since by me it shows the same number before and after GC.

 Console.WriteLine("Number of instances: {0}", Book.readNumber());
 b2 = null;
 GC.Collect();

 Console.WriteLine("Number of instances after GC: {0}", Book.readNumber());

I have 4 instances coming in through readNumber, but it still outputs 4 instead of 3 after GC the b2.

The requested Book class:

class Book
    {
        public String ISBN;
        public String Author;
        public double Price;
        private static int Quantity;
        public static int readNumber() { return Quantity; } 
        public Book ()
        {
            this.ISBN = "no ISBN"; this.Author = "no Author";  this.Price = 0.0;
            Quantity++;
        }
        ~Book() { Quantity--; }//first attempt to reduce the instance counter by 1
}

With Quantity, I count the instances from the class and save it in count for use in main.

Andreas
  • 89
  • 1
  • 11
  • 1
    How do you increment the value returned by readNumber and how do you expect this value to be decremented by a call to GC.Collect? Please post the Book class – Steve Jun 14 '17 at 17:26
  • Write the result of `GC.GetTotalMemory(false)` to see if the allocated memory went down. – Jay Buckman Jun 14 '17 at 17:41
  • My thought was if i delete b2 the instances counter would decrease. – Andreas Jun 14 '17 at 19:31

2 Answers2

0
class Book
{
    public String ISBN;
    public String Author;
    public double Price;
    private static int Quantity;
    public static int readNumber() { return Quantity; }
    public Book()
    {
        this.ISBN = "no ISBN"; this.Author = "no Author"; this.Price = 0.0;
        System.Threading.Interlocked.Increment(ref Quantity);
    }
    ~Book() { System.Threading.Interlocked.Decrement(ref Quantity); }
}

class Program
{
    static void Main()
    {
        Book one = new Book();
        Book two = new Book();
        Console.WriteLine(Book.readNumber());   // Outputs 2

        Console.ReadKey();
    }
}

You can't exactly control when the garbage collector will run. You can indicate to the garbage collector that it would be a good time to run by running GC.Collect() (however, I would not do this). I believe this will cover your case if you are concerned with what is in memory before and after garbage collection. With interlocked this will also help protect you from any instance count problems around concurrency with your objects as well.

I think it might also be fair to note that what is in memory vs what is being referenced or in use, are two different distinct counts. If a reference is no longer being used it doesn't mean the garbage collector comes by and immediately cleans it up but rather it can hang out in memory for a little while before the garbage collector comes a long.

IdahoSixString
  • 643
  • 10
  • 18
  • 1
    Making it `volatile` doesn't make the operation safe, because it's not. – Servy Jun 14 '17 at 17:48
  • @Servy volatile is a low level lock in itself and there was no requirement for thread safety as per the original question. However, since you have wanted to downvote I made it thread safe just for you. Read more here https://blogs.msdn.microsoft.com/ericlippert/2011/06/16/atomicity-volatility-and-immutability-are-different-part-three/ – IdahoSixString Jun 14 '17 at 17:54
  • 1
    Volatile is *not* performing a lock, no. It has a very specific semantic change, and that change doesn't make the operation you performed appropriate. As for the question not specifying that the code needs to actually work, and not produce the wrong value, I wouldn't expect that it would need to state that, I think that's a fair assumption to make for any question being asked. – Servy Jun 14 '17 at 17:55
  • Using a lock is extremely inefficient/slow (in the worst case it could be forced to do a kernel transition, see https://stackoverflow.com/a/14923685/7565574) if you just want to increment/decrement. It's much better to use the appropriate Interlocked methods to perform atomic operations. – ckuri Jun 14 '17 at 18:24
  • @Servy Since you seem to know so much about this please provide an appropriate answer. – IdahoSixString Jun 14 '17 at 18:59
  • @Servy Also please read the C# documentation. The volatile keyword indicates that a field might be modified by multiple threads that are executing at the same time. Fields that are declared volatile are not subject to compiler optimizations that assume access by a single thread. This ensures that the most up-to-date value is present in the field at all times. The volatile modifier is usually used for a field that is accessed by multiple threads without using the lock statement to serialize access. https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/volatile – IdahoSixString Jun 14 '17 at 19:05
  • @IdahoSixString So where does that say that it makes an increment or decrement operation on such a field logically atomic? (The documentation around `volatile` is also historically very full of errors, as the semantics are so complicated and also have changed numerous times, but that's not really relevant here, as that documentation still doesn't say that this operation is safe.) – Servy Jun 14 '17 at 19:07
  • @Servy You are correct that it does not explicitly say it is safe. However, just modified the answer once more to provide you with System.Threading.Interlocked.Incrament....This ensures an atomic operation on the referenced variable. Therefore, the extremely rare race condition of ++ and -- no longer will occur. – IdahoSixString Jun 14 '17 at 19:18
  • @ckuri There now uses System.Threading.Interlocked to ensure an atomic operation is occurring on the int with the count. – IdahoSixString Jun 14 '17 at 19:23
  • so what does System.Threading.Interlocked.Decrement(ref Quantity); exaxtly do? – Andreas Jun 15 '17 at 09:33
  • @Andreas Hopefully this answers your question.https://stackoverflow.com/questions/1828728/why-use-system-threading-interlocked-decrement-instead-of-minus – IdahoSixString Jun 15 '17 at 20:38
-3

Dont use static fields but if you insist :

class MyClass: IDisposable
    {
        public static int instanceCount;
        public MyClass()
        {
            instanceCount++;
        }

        public void Dispose()
        {
            instanceCount--;
        }
    }

and main function :

static void Main(string[] args)
        {
            MyClass a = new MyClass();
            MyClass b = new MyClass();
            Console.WriteLine(MyClass.instanceCount);
            b.Dispose();
            Console.WriteLine(MyClass.instanceCount);
            Console.ReadLine();
        }
  • Look at Interlock.Increment/Decrement to have better concurrency – Steve Jun 14 '17 at 18:15
  • 2
    `Dispose` has absolutely nothing to do with GC. Not really sure how calling `Dispose` answers the question about number of instances after GC (may be perfectly valid answer to *some other* question). – Alexei Levenkov Jun 14 '17 at 18:35