by dleskov on 2/22/18, 10:18 AM with 47 comments
by barrkel on 2/22/18, 11:52 AM
Thread stacks are effectively manually allocated blocks of memory. You create a thread, which allocates the stack, and as long as the thread lives, the stack is kept alive - it's self-sustaining. The thread must die by explicit programmatic action, which in turn will free its allocated block of stack memory.
Using finalizers at all is usually an anti-pattern in a GC world. The presence of finalizers is a very strong hint that the GC is being used to manage resources other than memory, something that GC is a poor fit for, because other resources almost certainly have no necessary correlation with memory pressure; and GCs usually only monitor GC heap memory pressure.
That's not to say that there aren't plenty of edge cases where you can end up with lots of false roots that artificially lengthen object lifetimes with a conservative GC. Putting a thread stack in your cycle of object references and relying on GC pressure to break the cycle isn't a strongly motivating one to my mind, though.
by aidenn0 on 2/22/18, 3:36 PM
It's earlier implementations were on RISC chips that had 24 or more GPRs so the implementation was simple: 2 stacks and divide the local registers in half for boxed and unboxed values. This obviously didn't work when porting to x86 which had far fewer registers.
The ARM port I believe uses the non-conservative approach, despite having 1 less register than x64 (the x64 port was derived from the x86 port so uses the same register assignments).
by dmytrish on 2/22/18, 1:01 PM
by bjourne on 2/22/18, 10:46 PM
Stack maps can be made a bit smaller by pushing and popping all registers from the stack during gc. That way, you only need to store the values of stack locations in them and not of individual registers.
Btw, the article is really good. This is the kind of stuff I keep coming back to HN for!
by naasking on 2/22/18, 1:28 PM
By comparison, the CLR is a much worse fit, because value types and stack/inline/fixed arrays means false positives would be much higher for some applications.
by SteveJS on 2/22/18, 5:46 PM
by willvarfar on 2/22/18, 10:50 AM
E.g. for each frame the compiler orders roots first and then other primitives. Then, as you enter the frame, write the number of roots to the stack. When the GC walks the stack it can see precisely which are roots.
by le-mark on 2/22/18, 3:01 PM
by PaulHoule on 2/22/18, 7:43 PM
In the 32-bit age, you ran into problems more and more as your heap approaches the GB range. At some point the probability that you end up with a false root that keeps a lot of garbage alive goes to 1.
In the 64-bit age we get a respite, although many systems don't really use 64-bit pointers.
by kazinator on 2/22/18, 8:38 PM
by jacksmith21006 on 2/22/18, 1:48 PM