I now have the time to give a longer explanation regarding GC, that anyone using Go might find interesting. There are some simplifications in the following, but they’re not significant.
At any given moment, the memory use of a program = live objects + garbage + unused heap. When a mutator (application thread) wants to allocate a object, it will use the ‘unused heap’. If there is no room in the unused heap, it has two choices: 1) grow the heap (ask the OS for more memory) 2) perform GC to collect garbage, adding the memory to the unused heap, then try the allocation again. If the runtime cannot allocate more memory from the OS (ulimit, CGroup/container limit, etc.) the only choice is to perform/wait (i.e. pause) until the GC frees enough garbage tp continue - this is usually called a “full GC/pause".If after the garbage collection(s) is performed, there is still not enough room for the object in the unused heap - then you will get an out of memory panic/exception. It is trivial to see that if the live object size reaches the process maximum memory there is no choice but to terminate/panic/OOME. If there is garbage to be collected, the mutator might pause indefinitely waiting for enough free memory (although in most cases, there is a limit here, after which an OOME will be thrown/panic). But there is another piece to the puzzle - the one Java was always hammered on in the beginning - incremental GC pauses. To understand these, the important aspects are the “allocation rate” - the rate at which the mutators threads generate garbage (e.g. MB/sec), and the “collection rate”, the rate at which the GC threads can find and turn the garbage into ‘free memory’ - move the memory on the unused heap. In a typical GC environment, the GC happens while the mutator threads run. There are two mains types of GC, 1) stop the world, and 2) concurrent. The “stop the world” collectors pause all mutator threads, use all available processors, and run for a period X to try and free as much garbage as possible. These can be very efficient, but the longer the mutators are paused, the greater latency. So often the goal is to minimize X - but if the allocation rate is exceeding the collection rate at X, then X needs to be increased, OR, if it isn’t then eventually a "full GC/pause” will happen. For concurrent GC, there is still a pause time Y, but it is not related to how much garbage can be collected, and is usually very short. It is typically only affected by the number of active mutator threads and their stack sizes. The pause is usually very small, only long enough to sample the threads stacks and some other housekeeping, and then the collector threads continue concurrently with the mutator threads. This is where things get interesting. If the too many GC threads run, or run too long, it starves the mutators threads from running (since they share the available processors) - this is not necessarily a pause, it is a scheduling/latency issue imposed by the OS - so typically the mutators threads have priority over the GC threads in low pressure scenarios. After the GC threads complete their cycle, the system has determined the current “collection rate”. If the system determines the allocation rate is exceeding the collection rate, that doesn’t mean the system is going to run out of memory - it can always pause the mutators longer to collect more garbage (and stop them from generating garbage), or pause them more often (run more GC cycles), or increase the priority of the GC threads. This is why I stated that even though the pause time is not affected by the heap size, the mutators might be paused more frequently, causing latency/throughput issues. The efficiency of the GC algorithm, plus available CPU, determines the collection rate. This is why having ample head-room (free heap) is very important for performance in GC systems, because often the mutator ‘allocation rate’ is not constant (e.g. heavy Internet traffic during the day, spikes, etc. lower at night), so if it has enough room to avoid more/longer GC cycles by just allocating memory from the heap/OS it will (to avoid pauses, more CPU for mutators), then wait for periods of lower activity to use more CPU for GC to collect the garbage. *** Btw, technically with the concurrent collector, a pause phase is not required. The Azul C4 collector is pauseless in the design, but currently uses a small pause phase for simplicity of implementation. There is the real possibility in the future, given hardware advances, that all collectors may become pauseless - but that still doesn’t mean there won’t be a pause due to full GC - it’s a throughput & latency issue. > On Sep 12, 2018, at 9:14 AM, Ian Lance Taylor <i...@golang.org> wrote: > > On Wed, Sep 12, 2018 at 7:07 AM, robert engels <reng...@ix.netcom.com> wrote: >> With the Azul VM (and I believe the G1 collector in Java), the VM is >> definitely aware of memory pressure as it approaches the maximum limit - >> then it will increase the concurrent GC activity trying to avoid a potential >> huge pause if the limit was reached - so throughput is lowered. >> >> I would think the Go GC needs to do this as well in order to limit pause >> times. > > That does not sound precisely correct to me. The Go GC pause time is > independent of the heap size, and I would expect that Java's pause > time is as well. It's certainly true that Go could run the GC harder > when coming closer to a memory limit, which will tend to starve the > program, but it won't actually pause the program. As I mentioned > earlier, though, when the program is coming close to its memory > limits, it needs to react. If it's a network server, it needs to stop > accepting new connections. If it has cached data, it needs to discard > it. And so forth. The GC can try harder but if the program keeps > allocating more memory there is nothing the GC can do to save it. > > Ian > > -- > You received this message because you are subscribed to the Google Groups > "golang-nuts" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to golang-nuts+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/d/optout. -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.