6 ms·
What? Are Go interfaces and strings stack allocated? If not, this assertion looks dubious to me.
by ernst_klim 8y ago
What? Are Go interfaces and strings stack allocated? If not, this assertion looks dubious to me.
- steveklabnik 8y agoLet me say that I am not super familiar with the Go source, and so this may be wrong. Please correct me if this is true! My understanding is, the string itself is on the stack, but the data is on the heap https://github.com/golang/go/blob/2f2e8f9c81d899d8eefb1f2f98ce5c90976c4f61/src/runtime/string.go#L217-L220 https://github.com/golang/go/blob/2f2e8f9c81d899d8eefb1f2f98... Likewise, the "double pointer" of an interface is on the stack, but points to the heap: https://research.swtch.com/interfaces https://research.swtch.com/interfaces So, from a lifetime perspective, it's still pretty stack-like. If you make a string, and you don't refer to it from outside the function, it's gonna get noticed by escape analysis and be like anything else on the stack.
- ernst_klim 8y ago>the string itself is on the stack, but the data is on the heap So both are regular heap allocated values? So the majority of data is still stack (edit: heap) allocated (safe ints, floats and some simple structs, I suppose), so I fail to see how ``young objects live and die young on the stack''. It is true that some GCed languages does not have stack allocation at all, but many of them (java, haskell) have very powerful inlining capabilities to compensate it. I still don't understand why I don't need generations for allocating to-die-soon strings in a more efficient ways.
- pcwalton 8y agoYou are correct. Properly-implemented copying generational GCs are the only things that work to optimize allocation of strings, arrays, and objects judged to be escaping.
- steveklabnik 8y ago> So the majority of data is still stack (edit: heap) It doesn't matter that the string's data lives somewhere else. It matters where what owns it lives. The string itself, that is, the pointer and length, both live on the stack, and so, it ends up working like a stack allocated value. > I still don't understand why I don't need generations for allocating to-die-soon strings in a more efficient ways. Because the string itself isn't on the heap, and therefore, a generation wouldn't help it.
- mike_hearn 8y agoHow does that follow? If the string is a pointer to a heap array plus a length, then "it ends up working like a stack allocated value" would only be true if the underlying byte array was freed immediately when a scope ended. But that isn't the case because the heap is a GCd heap, Go doesn't have explicit frees. This is one sort of reason why stack allocation isn't a cure-all - stack allocating a wrapper doesn't help you much if the bulk of the data is still in the heap. What can help, in this situation, is if the compiler can prove (or speculate that) the array is small and then scalar replace the array. However I think doing that sort of thing requires a profiling JIT compiler because statically proving array sizes ahead of time is very difficult.
- steveklabnik 8y agoI’m guess I am assuming that it would be freed at the end of the scope. As I said upthread, it’s entirely possible that I’m incorrect here. This kind of thing happens in Ruby a lot; the VM will allocate stuff through malloc that never actually hits the GC; see Aaron Patterson's work.
- mike_hearn 8y agoI've never seen a GCd heap where you can explicitly free things outside of the Boehm conservative GC. I haven't seen any reference to Go supporting such a thing either. I think all that happens when a string goes out of scope is that the byte array becomes eligible for GC.
- yencabulator 8y ago> My understanding is, the string itself is on the stack, but the data is on the heap What makes you say that? Pointers to stack are totally possible in Go; just because the `str` field is a pointer doesn't mean it points to the heap.
- colonelxc 8y agoGo uses escape analysis at compile time to determine where to allocate each variable. If the variable is shown to not 'escape' the current goroutine's stack, then it is can be allocated on the stack. Otherwise it is said that the value 'escapes' to the heap. You can see what the escape analysis has determined for each variable by building like so: go build -gcflags "-m" Unfortunately it can be hard to know by just looking at code if something will be stack or heap allocated. You can allocate a struct and take its pointer, and have it still end up on the stack, for instance (which is good). You can also have seemingly obvious flows for a variable that the analyzer can't reason about and it escapes when you didn't think it would. You really just have to run the tool to see if you want to be sure.
- ychen306 8y agoI am not disagreeing with you but am confused on the issue. Java also has escape analysis, so I wonder what makes the difference here.
- voidlogic 8y agoJava used to default to heap allocation for pretty much everything, but added escape analysis as an optimization. For Go escape analysis as been part of it since day 1. Basically Java is heap by default that uses the stack when it can. Go uses the stack unless it determines it must use the heap.
- bbatha 8y agoMost go data is value types that can be assumed to be on the stack, unless you introduce a pointer explicitly or use one of the referenceish types like interface, string, array or map. Its very easy for both the programmer and the compiler to see when data escapes. Java on the other hand is pathologically bad for escape analysis, outside of ints and floats literally every object in java is presumed to be on the heap. Even C# has less heap pressure than java typically because of more value type options, and not type erasing greatly improves the JIT's escape analysis. After sufficient JITing Java may reach go level of garbage generation, it may not: but its not deterministic.