Hacker News new | ask | show | jobs
by ltta 4234 days ago
As far as I know ARC (like manual memory managenent) can lead to release cascades that can also cause application delays in games eg. Something you have to watch for. Malloc() and free() and equivalents do quite a bit of work under the hood (check today's linked tcmalloc article for example) that takes time.
1 comments

Release cascades can be a big problem even with plain old RAII in C++. I once diagnosed a performance issue in a C++ network server where the server listening thread would sometimes temporarily hang when clients disconnected. The culprit turned out to be the destructor of a large std::map that directly and indirectly accounted for tens of millions of heap-allocated objects that had to be individually destructed and freed. It's very rare for destructors to have intentional global side effects, so this kind of work could almost always have been done asynchronously, at least in principle.

An unfortunately common symptom of large C++ applications is that they take forever to shut down because they insist on calling destructors on everything in the known universe. In reality, most applications only have a tiny handful of resources that you truly have to release yourself at shut down, and memory certainly is not one of them.

So, what do? Perhapsh mark a object as "kill me without mercy?"
If the mass objects have trivial (ignorable) destructors and don't own any heap-allocated subobjects then it's not a problem. You can have top-level objects like levels or documents or sessions own everything directly and indirectly under them and allocate those things out of a private heap that can ideally be reused but otherwise be bulk freed in a few calls. Unfortunately, the standard C++ philosophy around RAII and value semantics works against this. Since there is something to be said for the elegance and upfront convenience of the value semantics approach, it comes down to a trade-off.
So, if I design a language, how design it well? I suspect is easy to kill all values (ints, str, etc) and arrays/list of it. But how know what is a "don't own any heap-allocated subobject" (sorry to ask if this is obvious, I have not deep experience in this matters)?

Is not circular references the real problem? And what when the object reference a resource like a file/handle/database/etc?

So, could be good idea to mark objects like this (maybe in separated areas of memory?):

Instant kill: Ints, Strs, Bools, Array of all of this

Safe destructors: If it hold a resource (file, handle)

But don't know what to do for objects like Customer.Orders = List<Orders>[Order1.Customer]

This is not necessarily a language-level decision (except for GC, it helps in this case spread out the deallocations when using incremental GC).

You just have to be aware what happens in the destructor when you manually free an object. Libraries can make knowing this more difficult.

I guess you could build incremental alloc/release pools (even with reuse) or such things but it comes down to being aware of the problem as described and avoiding cascaded releases.

And to be honest this is not a super common problem but it can happen.

This is just an idea, but how about adding a function to your allocator that turns all future deallocations into a no-op?