Hacker News new | ask | show | jobs
by Skinney 1978 days ago
With a GC like the one used in .NET, «deleting» an object is a noop. It wouldn’t give you any benefits over not deleting it.

You only pay a price for living things (as in, some object holding a reference to it), the rest is free.

2 comments

I guess I could provide a bit more info.

A generational GC as the one in .NET allocates memory up-front, then passes out references/pointers from that allready allocated memory.

When the GC is getting close to the end of the pre-allocated memory, it will analyse all living objects (the objects it can reach from the stack and global variables, and objects referenced by those objects) and copies them over to a different area of memory (generation).

The area the memory was copied from, is now all garbage, and can be overwritten by new objects.

I guess you could say that instead of collecting garbage, it de-fragments your living objects.

If the GC still doesn’t have enough memory, it will try to allocate more.

In any case. The cost of a generational garbage collector is associated with living objects, not dead ones, so manually deleting doesn’t make sense.

> The cost of a generational garbage collector is associated with living objects, not dead ones, so manually deleting doesn’t make sense.

This is true in the context of the discussion, and in general for copying garbage collectors, but it's not always true. Copying is the most common way (and the current .NET way) to implement at least the Eden generation of generational collection, but it could be implemented in other ways.

Just as a clarification, "Copying is the most common way (and the current .NET way)" is not the case - .NET GC is generational but it does not promote through generations by copying.
True. Go, for instance, does not use a generational garbage collector. I also believe that Java’s Zgc is not generational, yet.

But I find it most helpful to instead focus on the current context, otherwise I’d be spending all typing.

I'm talking about generational non-copying collectors (possible, but certainly not common). Examples of non-generational collectors are non sequitur. You're talking the diagonally opposite corner of the square diagram. (What's the name of those 4-part square diagrams? I forget the name of the guy they're named after.)

For instance, you could have a mark-and-sweep collector that would mark everything and then first sweep just the most recently created arena, and only do a full sweep if not enough space was freed. It wouldn't be perfectly generational unless it was also compacting, but the youngest arena might be a decent heuristic. Or, for the cost of one pointer in every object header, the GC could keep a singly linked list of the youngest generation.

I don't think it's a great idea, but you can do a non-moving generational GC if you want to interact with C/C++ without forcing pinning/un-pinning of objects (or forcing C/C++ to only interact with GC'd objects via pinned handles).

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.

Ideally such handles are also wrapped in SafeHandle classes.
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.