5 ms·
Those articles claim that these problems don't exist in Go, but they don't really explain why, for many of them. The one part that is clear indeed is that the
by tsimionescu 2y ago
Those articles claim that these problems don't exist in Go, but they don't really explain why, for many of them.
The one part that is clear indeed is that the pervasive use of value types in Go reduces the amount of garbage that gets generated compared to Java or C#, and, of course, less garbage means less time spent on GC.
The claims about memory fragmentation are less clear though: the first article just says that it's not a problem; and the second one does have a segment dedicated to it, but that segment gets fragmentation completely mixed up with memory locality, an entirely unrelated concept. It later claims that certain allocators are known to not suffer from fragmentation issues, but given the previous confusion, I'm not sure how seriously to take this. It also doesn't say if Go actually uses those allocators or not.
As for the diagnostics, I gave a specific example of a very commonly needed diagnostic, identifying GC roots to understand why a memory leak is occurring, that Go simply doesn't provide (someone else suggested a third party package that might help). I am well aware of the basics provided in the article you linked, and they don't even discuss this. For whatever bizarre reason, the Go memory tools don't provide this info (that the GC obviously needs to determine in its operation) - probably another victim in their quest of making it easier to implement the runtime.
- lossolo 2y ago> The claims about memory fragmentation are less clear though As to memory fragmentation, memory allocations are grouped by size, so when objects of the same size are allocated and freed, the memory can be reused efficiently without causing fragmentation. So Go's memory allocator organizes memory into size classes, which are predefined blocks of different sizes. When an object is allocated, it fits into the smallest available size class that can accommodate it. This reduces internal fragmentation (unused space within allocated blocks). > As for the diagnostics, I gave a specific example of a very commonly needed diagnostic, identifying GC roots to understand why a memory leak is occurring, that Go simply doesn't provide (someone else suggested a third party package that might help). I am well aware of the basics provided in the article you linked, and they don't even discuss this. For whatever bizarre reason, the Go memory tools don't provide this info (that the GC obviously needs to determine in its operation) - probably another victim in their quest of making it easier to implement the runtime. Can't you just use pprof to find memory leaks?[1] You can even get a diagram showing where allocations occur and see which lines of code allocate the most memory. In all the cases where I was investigating memory leaks, pprof was sufficient. Do you have any common examples where this wouldn't work? 1. https://www.codereliant.io/memory-leaks-with-pprof/ https://www.codereliant.io/memory-leaks-with-pprof/
- tsimionescu 2y agoPprof shows where allocations happen, which doesn't tell you why an object is still present in memory, who is holding onto it. I've had to trawl through lots of third-party code to figure out who was holding onto some strings, for example. "Who originally allocated X" and "who is holding onto the last reference to X" are not directly linked concepts. Especially in a language that is happy to leak an entire array if a slice is holding onto three elements of it.
- lossolo 2y agoI see, thanks. Well, I've had similar problems, though not many. Maybe we just design things more carefully, but whenever we did encounter issues, knowing where the allocation happened was 90% of the investigation. The rest was just using the IDE features to see all possible references to that variable and go through the graph. At least that's my experience.
- neonsunset 2y agoA lot of structs or, say, slices in Go escape just as much. Or the interface implementations which get boxed. You have to pick any workload that is even remotely allocation/collection throughput sensitive and limitations of Go's GC become immediately apparent. What is not immediately apparent is that Go offers limited avenues to address this. It's a good design for its purpose especially given that it is much simpler and self-hosted when compared to Java's and .NET's GC impls, but assigning the attributes to it that never were there nor were pursued by its authors is just bad, but sadly common with some here, look.
- lossolo 2y agoIs your opinion grounded in theory or practice? As a practitioner running Go in a high-volume, low-latency environment with tens of millions of allocations per second, we haven't found GC to be a problem. The median pause time is actually around 130 microseconds per collection. We're using Go in network services. Maybe your use case differs from ours, but at the end of the day, you pick the right tool for the job, and programming languages are just tools. We believe we picked the right one.
- neonsunset 2y agoThis is a rather strong claim not supported by real-world data on how Go’s GC behaves under load. Which, in fact, isn’t the first time for your account as search across the discussion history indicates so you do you. Sustaining very small allocations with median period of say 66ns (and I’m going to be charitable here) is not that difficult with single-threaded workload. Things get interesting as it becomes multithreaded and object graph becomes more complex. As I said before, on any microbenchmark that is meant to evaluate allocation+collection throughput Go performs worse than the competition and there is no workaround to this because that’s just how its GC is designed: https://benchmarksgame-team.pages.debian.net/benchmarksgame/performance/binarytrees.html https://benchmarksgame-team.pages.debian.net/benchmarksgame/... And this is without even involving any sort of cross-thread data sharing between the allocations. It’s okay to praise things you like and criticize those you don’t, but it can be kept within the realm of actual pros and cons of a particular technology.
- _rlh 2y agoGo's defrag techniques and why they work are discussed in the Hoard papers and have proven their value not only in Go but in most malloc implementations. There is a relationship between cache locality, moving colocated objects to different cache lines to control fragmentation, value types, and interior pointers. Perhaps it is subtle but cache optimization is real important for performance and is not ignored by Go in the language spec or the runtime implementation.