No I had instances where I deleted an object, wanted to allocate a new one, only one of these would fit in memory, and got an outofmemory exception because the GC didn't kick in between the two. So it is not equivalent.
The GC kicks in when allocating a new object and it decides it needs more memory. GC likely kicked in as you allocated the last object, and after the collection phase, still didn’t have enough memory and so failed.
I believe objects with destructors can keep an object alive for an extra collection phase, but I believe if that’s the case it can easily be solved with a using block before allocating the next object.
No that wasn't the problem. You should be able to test it yourself for instance by opening and dereferencing a lot of system.drawing images but not disposing them. You will most likely get an out of memory exception (unless the behavior changed in the last couple of years, and I have not tested it on .net core). Unfortunately the GC doesn't immediatly kick in when you have a memory pressure and you will get an out of memory exception even though there is a lot of garbage ready to be collected (and in my case it was managed objects, not images).
I want to point out that disposing resources (using the IDisposable) interface does not free memory (unless the memory isn't managed but then it's not related to garbage collection).
A System.Drawing.Image holds operating system resources (GDI+) and these resources are released when the Image instance is disposed. If the instance isn't explicitly disposed then the finalizer will do it but this only happens when the garbage collector collects the Image instance.
Allocating many Image instances without disposing them might exhaust the available resources (Windows bitmap handles or whatever) but the garbage collector doesn't see any memory pressure and does not perform any collection so the finalizer does not dispose Image instances that are no longer used.
The garbage collector has an API (GC.AddMemoryPressure) where an object that has unmanaged resources can signal that it's consuming additional memory to inform the garbage collector's decision of when to perform a collection.
Yeah, which is why I mentioned the thing about destructors. The first GC will schedule an object to be disposed/destroyed, but still keep it around for abit.
It’s true that in that case it makes sense to «delete» an object. But in that case you can either use a using block or manually call Dispose, right?
For an unmanaged object you can use Dispose/using, but for a managed object, I am not aware of any way to explicitly delete it from memory once it has been dereferenced other than calling gc.collect.
For unmanaged object I am surprised it would keep it in memory for a bit since it is also the mean by which you release any lock on a file or a connection. If you don't execute it straight away, you potentially create bugs.
I think the reason why Microsoft was telling people not to call gc.collect is that it interferes with the optimisations and heuristics that the garbage collector maintains to optimise when to do a GC. But I must say I didn't notice any abnormal behaviour when I did. But I would only do if I absolutely have to.