http://ruizeng.net/golang-gc-internals/Summary
In the actual use of the go language in the process, encountered some seemingly strange memory consumption, and decided to go to the garbage collection model to do some research. This paper summarizes the results of the study.
What is garbage collection?
Once upon a while, memory management was a major challenge for programmers to develop applications. In a traditional system-level programming language (mainly referred to C + +), the programmer must manage the memory carefully and control the memory application and release. A bit of carelessness can create a memory leak, a problem that is difficult to spot and hard to locate, and has been a nightmare for developers. How to solve the problem of this headache? In the past, two approaches were generally used:
- Memory Leak Detection Tool. The principle of this kind of tool is usually static code scanning, through the scanner to detect possible memory leaks of the code snippet. However, the detection tools inevitably have omissions and deficiencies, can only play a supplementary role.
- Smart pointers. This is an automatic memory management method introduced in C + +, which refers to objects by a pointer object that has automatic memory management, which is the purpose for which the programmer does not pay much attention to the release of memory and to the memory for automatic release. This approach is the most widely used approach, but there is a certain amount of learning cost to programmers (not native support at the language level), and memory leaks cannot be avoided once forgotten scenarios are in use.
To solve this problem, almost all the new languages (java,python,php, etc.) that were developed later introduced automatic memory management at the language level – that is, the user of the language only uses memory-focused applications without concern for the release of memory, which is freed by virtual machines Machine) or run time (runtime) for automatic management. The behavior of automatic recycling of memory resources that are no longer used is known as garbage collection.
Common garbage Collection method reference count (reference counting)
This is the simplest kind of garbage collection algorithm, similar to the smart pointers mentioned earlier. A reference count is maintained on each object, and the reference count of the referenced object is automatically reduced by one when the object that references the object is destroyed or updated, and the reference count is automatically added one when the referenced object is created or assigned to another object. Objects are reclaimed immediately when the reference count is 0.
The advantage of this approach is that it is simple to implement, and that memory recovery is timely. This algorithm is widely used in systems with high memory tension and real-time, such as iOS cocoa Framework, Php,python, etc. The simple reference counting algorithm also has obvious drawbacks:
- Frequent update of reference counts reduces performance. A simple workaround is for the compiler to merge adjacent reference count updates into one update, and one way to do this is to not count the frequently occurring temporary variable references, but to decide whether or not to dispose of the temporary object references by scanning the stack when the reference reaches 0. And so there are many other ways, specifically can be referred to here.
- Circular reference issues. Objects in the reference chain cannot be freed when circular references occur between objects. The most obvious solution is to avoid circular references, such as the cocoa introduction of the strong pointer and the weak pointer two pointer types. Or the system detects circular references and actively breaks the loop chain. Of course, this also increases the complexity of garbage collection.
Mark-Clear (Mark and sweep)
The method is divided into two steps, where the tag iterates through all the referenced objects starting from the root variable, marking the objects that can be accessed through the application traversal as "referenced", clearing after the mark is complete, and reclaiming the memory that is not marked (recycling may be accompanied by defragmentation operations). This method solves the lack of reference counting, but there are obvious problems: every start of garbage collection will suspend all the current normal code execution, recycling is greatly reduced system responsiveness! Of course, there are many variants of the mark&sweep algorithm (such as tri-color notation) that optimize the problem.
Generational collection (generation)
After a lot of practical observation, most objects in object-oriented programming languages have very short life cycles. The basic idea of generational collection is to divide the heap into two or more spaces called Generations (generation). Newly created objects are stored in the new generation (young generation) (generally, the Cenozoic size is much smaller than the old age), and as garbage collection repeats, objects with longer lifecycles will be lifted (promotion) into the old age. Therefore, the new generation garbage collection and the old age garbage collection two kinds of different garbage collection methods came into being, respectively, to perform garbage collection for the objects in their space. The new generation of garbage collection is very fast, a few orders of magnitude faster than the old age, even if the new generation of garbage collection is more frequent, the implementation efficiency is still better than the old garbage collection, this is because most objects life cycle is very short, no need to ascend to the old age.
Go's garbage collector
The general use of Go language garbage collection is the classic mark and sweep algorithm.
-
- Prior to version 1.3, Golang's garbage collection algorithm was very primitive, and then its performance was widely criticized: go runtime under certain conditions (memory exceeds the threshold or regular such as 2min), suspend all Tasks execution, perform mark&sweep operation, Starts execution of all tasks when the operation is complete. In a more memory-intensive scenario, the GO program will have a very noticeable lag when it comes to garbage collection (Stop the World). This delay is simply intolerable in a background service process that requires high response times! Many teams in the production environment practice go language at home and abroad during this period have stepped on the GC pit more or less. The most common way to solve this problem was to control the amount of memory automatically allocated to reduce the GC load as soon as possible, and to handle the scenarios where large and high-frequency allocations of memory were handled by manual memory management.
-
- The 1.3 release starts with the Go team's continuous improvement and optimization of GC performance, which has become a major concern for every new release of Go release. In version 1.3, go runtime separates the mark and sweep operations, as before, pausing all task execution and starting Mark,mark to restart the suspended task as soon as it is completed, instead allowing the sweep task to be executed in parallel with other tasks in the same way as the normal coprocessor task. If running on a multi-core processor, go attempts to put the GC task on a separate core to run without affecting the execution of the business code as much as possible. The go team's own argument is to reduce the pause time by 50%-70%.
-
- There are not many performance changes to the GC for version 1.4 (currently the latest stable version). In version 1.4, many of the runtime's code replaced the native C language implementation and adopted the Go Language implementation, and a major change to the GC could be to achieve an accurate GC. C Language implementation in the GC can not get to the memory object information, so can not accurately distinguish between ordinary variables and pointers, only ordinary variables as pointers, if it happens that the ordinary variable point to the space there are other objects, then this object will not be recycled. While the Go language implementation is fully aware of the type information of the object, only the object pointed to by the pointer is traversed during markup, thus avoiding the heap memory waste (resolving about 10-30%) when C is implemented.
-
- The 1.5 release Go Team has also made a big improvement to the GC (1.4 has been planted with the introduction of write barrier), and the main goal of the official is to reduce latency. Go 1.5 The garbage collector that is being implemented is "non-generational, mobile, concurrent, tri-colored, marked clear garbage collectors." The generational algorithm mentioned above, is a good garbage collection management strategy, but the 1.5 version did not consider the implementation; I guess the reason is that the pace can not be too big, to gradually improve, go official also said in the 1.6 version of GC optimization to consider. The three-color notation described above is also introduced, and the mark operation of this method can be performed incrementally without having to scan the entire memory space each time, which can reduce the stop the world. As you can see, go all the way up to the 1.5 release, Go's garbage collection performance is also improving, but relatively mature garbage collection system (such as Java JVM and JavaScript V8), go need to optimize the path is still very long (but believe the future must be good ~).
Practical experience
The team in the practice of the Go language also encountered the most and most difficult problem is also memory problems (which GC-based), here to meet the problems and experience summarized under, Welcome to exchange discussion.
Go program memory consumption of large issues
This problem was found in our stress testing of backend services, when we simulated a large number of user requests to access back-office services, and the service modules were able to observe a significant increase in memory consumption. However, when the pressure measurement is stopped, the memory footprint does not decrease significantly. It took a long time to locate the problem, the use of gprof and other methods, still found no reason. Finally found that the original is normal ... There are two main reasons for this,
First, the Go garbage collection has a trigger threshold, which will grow with each memory usage becomes larger (such as the initial threshold is 10MB, the next time is 20MB, and the next time it becomes 40MB ...) ), if the GC go is not triggered for a long time, it will be actively triggered once (2min). When memory usage goes up at peak time, it is almost impossible to trigger a GC by threshold unless you continue to request memory, but wait up to 2min for the active GC to start to trigger the GC.
The second reason is that the go language simply tells the system that the memory is not needed to be used when returning the memory to the system, that it can be recycled, that the operating system will take a "procrastination" strategy, that it does not recycle immediately, but that it will not start recycling until the system is in a tight state of memory.
The issue of long GC time
For back-end programs that require user response events, the Stop the world part-time Golang GC is a nightmare. According to the above, the 1.5 version of Go to complete the above improvements should be a lot of GC performance, but all garbage-collected languages are inevitably faced with performance degradation in GC, for this we should try to avoid frequent creation of temporary heap objects (such as &abc{}, new, make, etc.) to reduce the amount of time spent on garbage collection, consider reusing the array cache for temporary objects that require frequent use; many people use CGO methods to manage their own memory and bypass garbage collection, which is not recommended unless the individual is not recommending it (easily causing unpredictable problems). Of course, the circumstances can still be considered, the effect of this trick is still very obvious ~
The problem of goroutine leaks
One of our services needs to handle a lot of long connection requests, when implemented, for each long connection request each open a read and write to the process, all using endless for loop to continuously process the sending and receiving data. When the connection is closed at the remote end, if the two processes are not processed, they will still run and the channel used will not be released ... It is important to note that when you do not use the process, you must close the channel that he relies on and determine whether the channel is closed by the re-process to ensure its exit.
http://wangzhezhe.github.io/blog/2016/04/30/golang-gc/
GOLANG-GC Basic Knowledge
APR -TH, 8:02 PM | COMMENTS
This section mainly introduces some of the Golang GC's introductory knowledge, because GC content involves a lot more, 1.1 point slowly finishing.
Golang Background of GC
- Golang is based on the garbage collection language, which is the principle of its design.
- As a garbage collector language, the efficiency of GC and program interaction can affect the efficiency of the whole program.
- In general, the memory management of the program itself affects the efficiency of GC and program, and even the performance bottleneck.
Issues related to GC Golang
This is the main parameter:
Http://morsmachine.dk/machine-gc
Was written in 14, it is estimated that at that time the GC mechanism is relatively simple, the new version of Golang to the GC changes should be larger
And the relevant part of the go language reading note about Golang GC.
About memory leaks
The word "Memory Leak" appears to be familiar, but it has never actually seen its exact meaning.
memory leaks , from the perspective of the operating system, the image of the metaphor is "the operating system can be provided to all processes of storage space (virtual memory space) is being squeezed by a process", the cause is the program in the run time, will continue to dynamically open up storage space, These storage spaces have not been released in time after the end of the run. After the application allocates a certain memory, due to the design error, it will cause the program to lose control of the memory, resulting in a waste of memory space.
If the program in the storage space to request a piece of memory, after the end of the program, the memory space is not released, and the corresponding program does not have a good GC mechanism to the application of the space to reclaim, which will lead to memory leaks.
From the user's point of view, memory leaks are inherently harmless, as this is not an impact on user functionality, but "memory leaks"
For languages such as C and C + + that do not have garbage Collection, we focus primarily on two types of memory leaks:
-
Heap memory leak (heap leak). The memory refers to the allocation of a piece of memory from the heap, such as Malloc,realloc new, as required by the program, and must be deleted by calling the corresponding free or delete after completion. If the program's design errors cause this part of the memory to not be released, then this memory will not be used, resulting in heap Leak.
-
System Resource Leakage (Resource Leak). Mainly refers to the program using the system allocation of resources such as bitmap,handle, socket and so does not use the corresponding function to release, resulting in system resources waste, serious can lead to lower system performance, system operation is not stable.
Memory leaks involve a lot of related issues, and there is no discussion here.
Common GC Patterns
Specific advantages and disadvantages can refer to this, here is just a general introduction.
-
Reference count (reference counting) Each object maintains a reference counter, and when an object referencing that object is destroyed or updated, the reference counter of the referenced object is automatically reduced by 1, when the object being applied is created, or assigned to another object, reference +1, the reference to 0 is recycled, Simple thinking, but frequent update of reference counters to reduce performance, there is a loop to reference (Php,python used)
-
Tag Cleanup (Mark and sweep) is used by Golang, which traverses all referenced objects from the root variable, clears after the tag, and reclaims unmarked objects: every garbage collection pauses all normal running code, and the system's responsiveness is greatly reduced. Various Mark&swamp variants (Tri-color labeling method) to mitigate performance problems.
-
Generational collection (generation) The JVM uses the idea of generational recycling. In object-oriented programming languages, the life cycle of most objects is very short. The basic idea of generational collection is to divide the heap into two or more spaces called Generations (generation). The newly created objects are stored in the new generation (young generation) (generally, the Cenozoic size is much smaller than the old age), and as garbage collection repeats, Objects with longer life cycles will be promoted (promotion) into the old age (a sort of idea is used here, which is also a basic idea of scientific thinking).
Therefore, the new generation of garbage collection and garbage collection in the old age two different ways of garbage collection came into being (first classified, then the right remedy), respectively, for the objects in their own space to perform garbage collection. The new generation of garbage collection is very fast, a few orders of magnitude faster than the old age, even if the new generation of garbage collection is more frequent, the implementation efficiency is still better than the old garbage collection, this is because most objects life cycle is very short, no need to ascend to the old age.
How the GC in Golang usually works
The GC in Golang is basically the idea of tag removal:
In the memory heap (because the heap's data structure is used to manage memory pages sometimes, so called heap memory), there are a series of objects that may be associated with other objects (references between these objects) a tracing garbage Collector will stop the program that was running at a certain point in time, and then it will scan the object collection (already known set of objects) that the runtime already knows, usually the global variables that exist in the stack and the various objects. The GC marks these objects, marks the states of those objects as attainable, finds all of them, from the current objects can reach the reference of objects elsewhere, and marks them as attainable objects, a step called Mark phase, the mark phase , the primary purpose of this step is to obtain state information for these objects.
Once all of these objects have been scanned, the GC acquires all unreachable objects (the state is unreachable objects) and recycles them, a step called sweep phase, which is the sweep phase .
The GC collects only those objects that are not marked as reachable. If the GC does not recognize a reference, it is possible to recycle an object that is still in use, causing the program to run incorrectly.
You can see the main three steps: Scan, recycle, sweep.
The garbage collection model in Golang is relatively simple compared to other languages.
Issues in the GC
The introduction of GC can be said to solve the problem of memory recycling. The newly developed language (java,python,php, etc.), when used, can make the user do not care about the release of the memory object, only need to care about the object's application, through the runtime or in the VM related operations, to achieve the effect of automatic management of memory space, This behavior of automatic recycling of memory resources that are no longer used is called garbage collection.
According to the previous statement, the ability to identify a reference normally is the basis for the GC to work properly, so the first question is how the GC should identify a reference?
The biggest problem: the identification of reference is more difficult, machine code difficult to know, how to be considered a reference. If missing a reference, it will make, the original is not ready to be free of the memory is now mistakenly lost, so the strategy is better not less.
One strategy is to think of all memory spaces as possible references (pointer values). This is known as the conservative garbage collector (Conservative garbage collector). The Boehm garbage collector in C is working like this. That is, the memory of ordinary variables as a pointer to the same processing, as far as possible to cover the case of all pointers, if it happens that the normal variable value of the space pointed to by other objects, then this object will not be recycled. While the Go language implementation is fully aware of the type information of the object, only the object pointed to by the pointer is traversed during markup, thus avoiding the heap memory waste (resolving about 10-30%) when C is implemented.
Tri-Color Marking
2014/6 1.3 introduces concurrent Cleanup (garbage collection and user logic concurrency execution?) )
2015/8 1.5 Introduction of Tri-Color labeling method
With regard to the introduction of concurrency cleanup, it is referenced here that in version 1.3, go runtime separates Mark and sweep operations, as before, pausing all tasks to execute and starting mark (mark, or the original program). When Mark finishes, it restarts the suspended task, and makes the sweep task parallel to the normal co-operation and executes with other tasks. If running on a multicore processor, go tries to put the GC task on a separate core to run without affecting the execution of the business code, and go team's own argument is to reduce the pause time by 50% to 70%.
The basic algorithm is the previously mentioned cleaning + recycling, Golang GC optimization of the core is to try to make STW (Stop the world) time is getting shorter.
How to Measure GC
So much has been said before, how to measure the efficiency of GC's star, to determine whether it has the effect of the operation of the program? The first way is to set GODEBUG environment variables, specifically can refer to this article, is really a good article: links, such as runningGODEBUG=gctrace=1 ./myserver, if you want to understand the output results, but also need to further analyze the principle of GC, the advantage of this article is that Clearly, the GC time of Golang is determined by which factors, so it is possible to take a different approach to increasing GC time:
According to the previous analysis, it is also known that GC in Golang is marked with a clear method, so the total time for GC is:
Tgc = Tseq + Tmark + Tsweep(t means time)
- TSEQ represents the time required to stop a user's goroutine and do some preparatory activities (usually small)
- Tmark is a heap tag time, and the token occurs when all users goroutine stop, so it can significantly affect the latency of processing
- Tsweep is a heap cleanup time, which usually occurs at the same time as a normal program, so it is not critical for latency
After the granularity is further subdivided, the specific concepts are still somewhat less understood:
- Related to Tmark: 1 garbage collection, the number of active objects in the heap, 2 the total amount of memory occupied by the active object with pointers 3 the number of pointers in the active object.
- Associated with Tsweep: 1 Total heap of memory 2 of total garbage in the heap
How to perform GC tuning (Gopher Convention Danny) Hard parameters
Problems involving algorithms always have some parameters. The GOGC parameter mainly controls the amount of memory used when the next GC starts .
For example, the current program uses 4M of memory (in this case, heap memory ), that is, the program is currently reachable memory 4m, when the program occupies the memory reached reachable* (1+gogc/100) =8m, the GC will be triggered, Start the related GC operation.
How to set the parameters of GOGC, according to the actual scene in the production situation, such as GOGC parameter promotion, to reduce the frequency of GC.
Small Tips
Want to have in-depth insights, the use of GDB is necessary, this article has compiled some of the introduction of GDB use of techniques.
Reducing object Allocation so-called reducing the allocation of objects, is actually as far as possible, object reuse. For example, two function definitions like the following:
|
func(r*Reader)Read()([]byte,error)
func(r*Reader)Read(buf[]byte)(int,error)
|
The first function has no formal parameters, returns a []byte] each time it is called, and the second function, at each invocation, is a buf []byte type Object, and then returns the number of bytes read in.
The first function allocates a space each time it is called, which creates additional pressure on the GC. The second function reuses the formal parameter declaration each time a di is called.
cliché string and []byte conversion between STIRNG and []byte, will cause the GC to stress through GDB, you can compare the data structure of the two:
type = struct []uint8 { uint8 *array; int len; int cap;}
type = struct string { uint8 *str; int len;}
When the two transitions occur, the underlying data node structure is duplicated, resulting in a lower GC efficiency. One way to resolve the strategy is to always use []byte, especially in the data transfer aspect, []byte also contains a number of valid operations that are commonly used by string. The other is to use a more low-level operation to directly convert, to avoid the occurrence of replication behavior. You can refer to the first part of performance optimization in "Rain Scar Academy", mainly using unsafe. Pointer directly to the conversion.
For the use of unsafe, the feeling can be individually sorted out an article, first the relevant information listed here http://studygolang.com/articles/685 intuitive, you can put unsafe. Pointer is understood as a void* in C + +, which is equivalent to a bridge of various types of pointers for conversion in Golang.
The underlying type of uintptr is int, which can load the value of the address that the pointer refers to. It can and unsafe. The main difference between pointer is that UINTPTR can participate in pointer operations, and unsafe. Pointer can only be converted by pointers and cannot be operated by pointers. If you want to use Golang for pointer operation, you can refer to this. When the specific pointer operation, we must first turn to the type of uintptr, in order to further calculate, such as how many offsets.
Small Use + connection string because the use of the + string connection will generate new objects, reducing the efficiency of GC, good way is through the APPEND function.
However, there is a disadvantage, such as the following code:
1 |
b := make([]int, 1024) b = append(b, 99) fmt.Println("len:", len(b), "cap:", cap(b)) |
After using the append operation, the space of the array increased from 1024 to 1312, so if you can know the length of the array beforehand, it is best to do a space planning operation when the space is initially allocated, which will increase the cost of some code management, and also reduce the pressure of GC and improve the efficiency of the Code.
Resources
Https://talks.golang.org/2015/go-gc.pdf
https://www.zhihu.com/question/21615032
Https://blog.golang.org/go15gc
Golang GC Chinese Primer (Summary comparison comprehensive includes Golang GC in different versions of the comparison likes) http://www.open-open.com/lib/view/open1435846881544.html (original)
Other garbage collection related articles
This introduction of the GC is more systematic: http://newhtml.net/v8-garbage-collection/
1.5 version of the garbage collector http://ruizeng.net/go-15-release-notes/
Memory Leak Reference http://blog.csdn.net/na_he/article/details/7429171
Go1.5 Source Analysis Https://github.com/qyuhen/book
An example of manually managing a Golang GC (more in-depth content) http://my.oschina.net/lubia/blog/175154
Golang Garbage Collection GC