Join the discussion

Write your take first — we'll ask for email only when you're ready to publish.

  • Hacker News
  • But if the objects being scanned are already located on the same page, aren’t we just wasting time managing the page and tracking the objects within it?
  • that's very interesting with some blunt ending
  • I never did think much about why they called it a heap until now :)
  • Tangential but this makes me think of a video about C# GC, and the developer switching to Swift to avoid it, in which the dev says they estimate the development of a pause-less GC to be 5B$ R&D away, does that ring the bell to anyone?
  • Ah it's from 2023, Miguel de Icaza : Swift Godot: Fixing the Multi-million dollar mistake: https://www.youtube.com/watch?v=tzt36EGKEZo

    Highly recommend.

  • According to Cliff Click, compilers and JVM + some other systems programming guy, Azul has a JVM with microseconds pause times at very large heap sizes (10s, 100s of GB?) and allocation rates. That is "pauseless" for gaming purposes.
  • The heap visualization is a great way to make GC behavior less abstract. It's interesting to see how much impact memory layout and cache locality can have, not just the GC algorithm itself.
  • Interesting and its noteworthy to observe that the new GC actually increases the rate of L3 misses slightly. For reference, L1 misses are quite fast, and often free since the CPU can cover up the empty slots with useful work, however L3 misses require a read from RAM which is an eternity in CPU cycles.

    My theory is that Go programs cover up the wait for L3 via SMT (which is still kinda common on server CPUs) and schedule work from another hardware thread.

    Such a technique might be less usable in less-threaded languages, or non-SMT CPUs and the new GC might end up being slower.

  • Excellent optimization technique: manually copying objects to a new slice so they don't prevent the GC from releasing memory by sitting right in the middle of a page it intends to free.
  • For anyone interested in this - https://www.youtube.com/watch?v=gPJkM95KpKo
  • thank you for the link, great and well-explained talk.
  • This was very interesting, but the ending was a bit abrupt. I was under the impression that there was something more that I was missing under the subscribe banner.

    But to the point of the article - are there cases in which manually moving objects around to compact them in a specific area is done with golang? I don't use golang that much, and I'm sure that there are very strong arguments for not compacting the heap post-GC, but I've always wondered how it avoids crashing in the 0.0001% of cases in which heap is defragmented in such a way that there's no way to allocate a new large object

  • Are you sure that it doesn't just crash in that case?

    Also, we have such a huge virtual memory space on 64 bit architectures that I can imagine that being involved somehow, but I don't know for sure.

  • It's because of what's in the second paragraph, which I will paste here for convenience:

    "Taking a step back, Go manages memory by allocating objects of the same size class (an object’s size is rounded up to the nearest size class) within a contiguous chunk (or span in Go terminology) of one or more 8KiB pages. Size-segregated allocation is common in some malloc implementations (like tcmalloc, which Go’s allocator descends from)."

    (No passive-aggressive snark about reading the article intended; it's a good question and I really am just pasting it for our convenience.)

    You can't get the inability to allocate some large object because there's a spray of small objects in its way, because the small objects don't share space with the large object. The large objects live in their own space, and the fragmented small objects can be efficiently utilized later by putting other small things in the empty space later.

    In the 64-bit world, you also don't have to worry about how you arrange things in physical RAM so one size doesn't end up impacting another. You can always allocate some suitably-sized new chunk of ram that's incredibly distant in the virtual address space and let the OS map that back to the real RAM. It may do something more clever than that because there is still 32-bit Go and that trick is less freeing in that scenario, but the principle would still hold. You don't get the inability to allocate a large object by small objects because they don't live in the same place. You might still "run out of RAM" before you've quite literally run out of RAM, but you'll get closer.

    And ultimately that's a problem shared by a lot of memory allocation schemes, not particular to GC or Go. For a lot of reasons, it's a good idea not to run resource usages right up to 100% if you can avoid it and you can expect across a wide range of resources types to encounter problems and expect to do a lot of careful work to make it possible to hit truly full utilization if you need it for some reason. Beyond computers, even... it's rarely a good idea to plan on 100% utilization of anything be it physical or electronic.

    by jerf