3 ms·
> the algorithms required to manage that "stack" memory are very similar to the algorithms for managing plain heap memory, and not at all like how (heavyweight
by 4ad 10y ago
> the algorithms required to manage that "stack" memory are very similar to the algorithms for managing plain heap memory, and not at all like how (heavyweight thread) stacks are managed.
No, this makes no sense. It's managed absolutely identical to how any plain stacks in C are managed. Stacks are created and destroyed when scheduling units (threads or goroutines) are created and destroyed. Whether that memory is allocated by some user level library or the kernel (albeit with POSIX threads, you can, and in fact it's common for thread memory to be allocated by the calling process beforehand) has absolutely no relevance whatsoever. All that matters is that the lifetime of the stack is 1:1 mapped to the lifetime of the schedulable entity.
Of course in Go goroutine stacks are dynamic, which is an optimization that makes Go programs use less memory, but that has no semantic effect.
- pron 10y agoThe thing that makes "plain" stacks easy to manage is that they're of constant size and long lifetime. If the the stack (and its schedulable entity) may be very short lived, and the stack itself may resize, then the stack requires the same (or very similar) dynamic memory management as a heap object. It is the precisely the semantics that make no difference here, but the actual behavior and lifetime of data in memory. It doesn't matter that the lifetime of the memory is that of the thread, if the lifetime of the thread is not at all like that of a heavyweight thread. From the perspective of the programmer it's a stack; from the perspective of the runtime -- it's a heap object (or close enough; there are some assumption you can make -- which Go makes -- that makes managing that memory a little easier, but those assumptions are, as usual, tradeoffs).
- 4ad 10y ago"Managing" stacks does not matter here. That's trivial. What matters is the lifetime of the data that lives on the stack. You correctly pointed out that in Java, the data on the stack represents a small percentage of all data, so escape analysis has a limited amount of effect on decreasing memory pressure. However, in Go, this percentage is much higher because there are orders of magnitude more stacks.
- pron 10y agoBut those stacks aren't stacks from a memory management perspective! They're just heap objects (that happen to be exposed to the user using a stack abstraction): The data in them is heap data, their lifetime is plain heap lifetime, and their memory management (which is what we're talking about and isn't at all trivial) is heap memory management (pretty much). It's just another way of abstracting objects via message passing. You can call your objects "stacks", but they're still the same objects. Let me take this to the extreme: Imagine a language with no assignable variables at all, but with processes and channels. In that language, you're able to create an arbitrary mutable variable as a process with a channel, that can get `set` or `get` messages. The value of the "variable" is kept in the process's "stack". In fact, this is pretty much how the process calculi work (one of which, CSP, is one of the inspirations behind Go's design). That this is how you choose to design your programming abstractions makes no difference to the runtime implementation. Your stacks would behave just like normal variables behave in languages with different abstractions.
- 4ad 10y ago> But those stacks aren't stacks from a memory management perspective! Yes they are, it doesn't matter who is allocating them, the kernel, or the program itself (which as I mentioned, also happens with POSIX threads). > The data in them is heap data No it isn't, not anymore than data in POSIX thread stacks is heap data. You do realise that the memory for POSIX thread stacks also comes from "the heap" (regardless of who is allocating it), and it's not from the process stack segment, right? > their lifetime is plain heap lifetime No, their lifetime is the lifetime of the function activation record, just like in C with POSIX threads. The lifetime of some value that lives on the stack is the lifetime of the stack frame, not the lifetime of the stack segment. > and their memory management (which is what we're talking about and isn't at all trivial) is heap memory management (pretty much). No, the memory management of values that live on the stack is stack memory management. Values live as long as their stack frame they belong to lives. Stack frame allocation is a pointer decrement, and stack frame deallocation is a stack frame increment (plus some amortised O(1) cost caused by copying stack mechanism). The lifetime of the stack segments themselves does indeed resemble (or is) heap-based memory allocation, but that is true in C, Java, Go, and any other language, and doesn't matter (or, if you take the position that it matters, it matters the same in all these languages and environments). And allocating stack segments is trivial. > It's just another way of abstracting objects via message passing. ??? Message passing!? Wat. Edit: > Let me take this to the extreme: [...] If you invent new language implementations and change the normal definition of various concepts, I guess anything can be true in that particular frame of reference...