C ++ memory management revolution (2): The most pocket Garbage Collector

Source: Internet
Author: User
This article has been migrated to: http://cpp.winxgui.com/cn:the-tiniest-gc-allocator-autofreealloc

Pocket Garbage Collector Xu Shiwei 2005-7-17Keywords: Memory Management garbage collection autofreeallockeyword: Memory manage, GC, garbage collection, autofreealloc OverviewC/C ++ is the most criticized, probably because there is no memory garbage collector (specifically, there is no standard Garbage Collector ). The main point of this article is to implement a minimum garbage collector with limited functions in C/C ++. The main feature of this garbage collector that distinguishes it from other garbage collectors is: 1. Small but practical. The number of lines of code in the garbage collector is about 100 (excluding blank lines), which is quite small. Its functions are also limited. But it is useful in many key scenarios. This garbage collector is designed for practical purposes and has become an important tool for programming with colleagues around me. 2. High Performance. What distinguishes it from other garbage collectors is that this pocket garbage collector will not only lead to performance degradation, but also improve the program's time performance (faster allocation) and space performance (memory space occupied is less than normal malloc/new ). This is also an important indicator of practicality. This algorithm is not complex. Many technical things have nothing to do if they are clear. Perhaps the important thing is their initiative. In fact, the pool component provided by boost [1] is also trying to provide automatic memory recovery capabilities with similar functions. However, the implementation is relatively complex and inefficient (based on the classic mempool technology [2]). Now, you may be anxious to see what the Garbage Collector looks like. Let's start to uncover the answer step by step. IdeasThe key to understanding the garbage collector is to understand its goal: to provide automatic memory recovery capabilities for a complex local process (algorithm. The so-called local process (algorithm) refers to the process that is complex but takes a short time during the running of the program [3]. For example, the search process of a search engine, the process of reading/saving a disk, and the process of displaying (drawing) the disk. In general, these processes may need to apply for a lot of memory, and there are many entry points for Memory Allocation Operations (that is, there are many calls to new ), if we call new every time, we need to consider where the delete operation should waste our valuable mental power in vain, so that we cannot focus our full efforts on the design of the algorithm itself. In this case, C/C ++ programmers envy languages with garbage collectors. If the algorithm is not complex, our programmers have the ability to control the matching relationship between new and delete operations. Moreover, this feeling of "Everything is under my control" gives us a sense of security [4] and satisfaction. Therefore, the center of gravity of the garbage collector is not to provide a theoretically functional automatic memory recovery mechanism. It only targets complicated local processes (algorithms) and provides them with the most effective memory management means. From the beginning of a local process, you just need to apply for and use the memory. After the entire algorithm is completed, most of the memory applied for in this process (which must be an exception for retaining the algorithm results ), no matter which step it applied for in the algorithm, it is automatically destroyed by the garbage collector at this end point. Let's draw a picture: Figure 1 SpecificationWe named the Garbage Collector autofreealloc. Its interface is simple and involves only two concepts: alloc and clear. Typedef Void(* Fndestructor)( Void* Pthis ); Class Autofreealloc{ Public:~ Autofreealloc(); // Destructor. Automatic Call Clear Release memory Void* Alloc ( Size_tCB ); // Similar Malloc (CB) Void* Alloc ( Size_tCB, FndestructorFN ); // Apply for memory and specify the destructor VoidClear (); // Analyze and release all allocated objects}; To facilitate the new operations (the implementation has been briefly introduced in the previous article), which is roughly as follows: Template<Class Type, Class Alloctype> Type* New ( Alloctype& Alloc ); // Similar New Type  Template<Class Type, Class Argtype1, Class Alloctype> Type* New ( Argtype1Arg1, Alloctype& Alloc ); // Similar New Type (arg1)  Template<Class Type, Class Alloctype> Type* Newarray ( Size_tCount, Alloctype& Alloc ); // Similar New Type [count]Example: AutofreeallocAlloc; Int* Intarray = ( Int*) Alloc. alloc ( Sizeof( Int) * Count ); Int* Intarray2 = newarray < Int> (Count, alloc ); Myclass* OBJ = new < Myclass> (Alloc ); Myclass* Objwitharg = new < Myclass> (Arg1, alloc ); Myclass* Objarray = newarray < Myclass> (Count, alloc); alloc. Clear (); //... // Now, you cannot access Intarray, OBJ, objwitharg, objarray And so on.   Memory Management Mechanism Class Autofreealloc{ Public: Enum{Blocksize = 2048 }; Private: Struct _ Memblock{ _ Memblock* Pprev; CharBuffer [blocksize] ;}; Enum{Headersize = Sizeof(_ Memblock)-Blocksize }; Char* M_begin; Char* M_end ;}; AutofreeallocThe class has only two variables related to memory management: m_begin and m_end. From the definition of variables, it is basically hard to understand. However, with the following picture, it is easy to understand more: Figure 2 the whole AutofreeallocApplied memory, passed through _ MemblockCreate a linked list. As long as you get the head of the linked list, you can traverse the entire memory chain and release all the applied memory. The head of the linked list (marked _ Chainheader), You can use M_beginCalculated as follows: _ Memblock* Autofreealloc: _ Chainheader () Const{ Return( _ Memblock*)( M_begin-Headersize);} To make the _ chainheader Initial Value Null, The constructor is written as follows: Autofreealloc:: Autofreealloc() {M_begin = m_end = ( Char*) Headersize ;}★Next we will consider the memory allocation process. There are three main situations in the alloc process: Void* Autofreealloc: Alloc ( Size_tCB ){ If(M_end-m_begin <CB ){ If(Cb> = blocksize ){ _ Memblock* Pheader = _ Chainheader();
_ Memblock* Pnew = ( _ Memblock*) M_alloc.allocate (headersize + CB );
If (pheader)
{
Pnew-> pprev = pheader-> pprev;
Pheader-> pprev = pnew;
}
Else
{
M_end = m_begin = pnew-> buffer;
Pnew-> pprev = NULL;
}
Return pnew-> buffer ;} Else{_ Memblock* Pnew = (_ Memblock*) Malloc ( Sizeof(_ Memblock); Pnew-> pprev = _ chainheader (); m_begin = pnew-> buffer; m_end = m_begin + blocksize ;}} ReturnM_end-= CB;} 1. The simplest case is that the current _ memblock still has enough free memory (free memory), namely:
M_end-m_begin> = CB
In this case, you only need to move m_end to CB bytes. Let's draw a picture as follows: Figure 3 2. when the current free memory of _ memblock is insufficient, we need to apply for a new _ memblock for use [5]. Apply for a new _ Memblock, We will encounter two more situations: a) the number of bytes requested (that is, CB) is smaller than the memory (Block Size) That one _ memblock can provide ).
In this case, we only need to use the _ memblock as the new current _ memblock to mount it to the linked list. The rest of the work is exactly the same as scenario 1. Figure 4 B) when the number of bytes requested by the memory (that is, CB) is greater than or equal to the number of bytes of a block, we need to apply for memory exceeding the normal length (blocksize) _ Memblock. All memory of the newly generated _ memblock is applied by the user. Therefore, we only need to modify the pprev pointer of _ chainheader to point to the newly applied _ memblock. M_begin and m_end remain unchanged (the current _ memblock is still the current _ Memblock). Figure 5★Next we will consider the memory release (clear) process. This process is very simple to traverse _ memblock and release all _ memblocks. The Code is as follows: Void Autofreealloc: Clear (){_ Memblock* Pheader = _ chainheader (); While(Pheader ){_ Memblock* Ptemp = pheader-> pprev; free (pheader); pheader = ptemp;} m_begin = m_end = ( Char*) Headersize ;} Automatic structure ProcessWe know that C ++ and other object-oriented languages introduce the construction and analysis processes for objects. This is an amazing invention. Only in this way can we ensure that the data of an object is complete and self-consistent throughout the process from its generation (just new) to its destruction. Because the garbage collector is responsible for object recycling, it does not only need to pay attention to the release of the memory applied by the object, but also needs to ensure that its destructor is called before the object is destroyed. To focus on the memory management process, we have removed all the code required for the automatic destructor process. To support automatic destructor, The autofreealloc class adds the following members: Class Autofreealloc{ Struct_ Destroynode{_ Destroynode* Pprev; FndestructorFndestroy ;};_ Destroynode* M_destroychain;}; if a class has an destructor, it must specify the Destructor while alloc memory. The Code is as follows: Void* Autofreealloc: Alloc ( Size_tCB, FndestructorFN ){_ Destroynode* Pnode = (_ Destroynode*) Alloc ( Sizeof(_ Destroynode) + CB); pnode-> fndestroy = FN; pnode-> pprev = m_destroychain; m_destroychain = pnode; ReturnPnode + 1;} as long as the memory applied through the alloc function, we can call the corresponding destructor in clear. Of course, the clear function requires additional code related to automatic destructor: Void Autofreealloc: Clear (){ While(M_destroychain) {m_destroychain-> fndestroy (m_destroychain + 1); m_destroychain = m_destroychain-> pprev ;} // The following describes the normal memory release process. ...}   Time Performance Analysis Void* Autofreealloc: Alloc ( Size_tCB); OOP brings about a memory problem: the object granularity is getting finer, and objects are basically small objects. This puts forward high requirements on the Performance of memory management. If the average object size is 32 bytes, only one operation meets the m_end-m_begin <CB condition for each 2048/32 = 64 operation. That is to say, in general (63/64 = 98.4% probability), The alloc operation only needs one subtraction operation to complete memory allocation. I said this is the fastest memory allocation algorithm in the world, and you may still be skeptical about it. However, I think it is very difficult to break through its performance limit. Void Autofreealloc: Clear (); Generally, a memory manager calls a free operation for one memory allocation operation. However, autofreealloc does not release each alloc, but instead _ Memblock. If the average object size is still 32 bytes, it is equivalent to merging 64 alloc operations to provide a corresponding free process.★Conclusion: The time performance of autofreealloc is about 64 times faster than that of common malloc/free. Spatial Performance AnalysisWe know that in general, in order to manage the memory blocks applied by the user, in addition to the CB bytes memory required by the user, the memory manager usually provides an additional header structure of the memory block, this header structure Concatenates the memory into a linked list. Generally, this header structure has at least two items (maybe more than one), as shown below: Struct Memheader{ Memheader* Pprev; Size_tCB ;}; it is still assumed that the average alloc memory is 32 bytes at a time. A malloc allocation process wastes 8/32 = 25% of memory. And because a large number of small objects exist, fragments in the whole memory (memory that is free but cannot be used) will be particularly serious. The alloc of autofreealloc has no additional overhead. The entire autofreealloc only has an additional pprev pointer when the _ memblock string is used as the linked list, and _ memblock is malloc, with an additional 8-byte overhead. Total waste (4 + 8)/2048 = 0.6% of memory, almost negligible. PostscriptAutofreealloc was developed in 100-5-21, with only lines of code. However, this component has achieved unprecedented success, and its application scope has gradually expanded, exceeding my expectation when I first implemented this component. I gradually calmed down and considered the principles contained in it. I gradually learned that its success is not the efficiency of its time and space performance, but the fact that it has helped C ++ programmers solve the biggest problem-memory management. Although, this solution is not complete. Autofreealloc is a starting point. From it, I understand that C ++'s new/delete is unreasonable. the Allocator introduced by STL is a starting point, it makes me understand that memory management has a strong region and the requirements for Allocator vary in different regions (local processes. We also mentioned an example in the previous article: when a document is opened and edited, it is not a partial process until it is finally closed? In the problem domain solved by autofreealloc, we obviously cannot regard it as a local process. But from other Allocator perspectives, is it possible to take it as a local process? Taking into account the defects of autofreealloc, we need a more powerful garbage collector. This is the component we will discuss next time. Finally, we still need to make it clear. It is difficult to implement a garbage collector like Java and C. It is the right way to provide Allocator with specific memory management capabilities.

[1] Please refer to boost official website http://www.boost.org /. [2] mempool technology is a very mature memory management technology, which is used by SGI-STL, boost, and other C ++ libraries. [3] whether or not to define a process as a local process depends entirely on the designer. For example, if a document is opened and edited until it is finally closed, is this complete process not a part of the process? In most cases, we think it is not a local process, but next we will discuss whether it is possible and how to treat it as a local process. [4] users who provide the language of the garbage collector obviously have the trouble of applying the garbage collector. For example, when C # Calls uncontrolled code (such as calling Win32 API), these problems become prominent, and a negligence leaves a potential risk. This is similar to the feeling that C/C ++ programmers regret that the language does not have a garbage collector. [5] The free memory of the current _ memblock is likely to exist, but it is less than CB bytes. Here we talk about memory fragments (memory piece): Although these fragments are not used by anyone, we discard them. Additional instructions:

The complete code of the autofreealloc component described in this article can be found in the winx library. You can also browse through the following link online:

Autofreealloc full source code

In addition, this article was written earlier. Although its specifications are the same as those of autofreealloc, the member function name has been changed:

Alloc-> allocate
Clear-> clear

This is because autofreealloc is incorporated into the stdext Library (this library can be independent of the winx interface library and is the basis of the winx interface library ). The naming style of the stdext library should be the same as that of STL.

Related Articles: C ++ memory management revolution

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.