In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-31 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article mainly explains "what are the knowledge points related to C++ fixed memory block allocator". The content in the article is simple and clear, and it is easy to learn and understand. Now please follow the editor's train of thought slowly and deeply. Let's study and learn what are the knowledge points related to C++ fixed memory block allocator.
Reclaim memory storage
The basic philosophy of the memory management model is that memory can be reclaimed when object memory is allocated. Once an object is created in memory, the memory it occupies cannot be reallocated. At the same time, memory should be able to be reclaimed, allowing objects of the same type to reuse this part of memory. I implemented a class called Allocator to demonstrate these techniques.
When an application uses the Allocator class for deletion, the memory space occupied by the object is freed for reuse, but not immediately released to the memory manager, which is retained in a linked list called a "release list" and re-allocated to the same type of object. For each request for memory allocation, the Allocaor class first checks to see if there is memory to be freed in the Free list. New memory is allocated only if there is no available memory space in the release list. Depending on the behavior of the desired Allocator class, memory storage uses global heap memory or static memory pools in three modes of operation.
1. Heap memory
two。 Heap memory pool
3. Static memory pool
Heap memory vs. Memory pool
The Allocator class can request new memory from heap memory or memory pool when the release list is empty. If you use a memory pool, you must determine the number of objects in advance. Make sure that the memory pool is large enough to accommodate all the objects you need to use. On the other hand, there is no limit to the amount of heap memory used-- you can construct as many objects as memory allows.
Heap memory mode allocates memory for objects on global heap memory. The release operation stores this piece in the "released list" for reuse. When the release list is empty, you need to create new memory on the heap memory. This approach provides dynamic memory allocation and release. The advantage is that memory blocks can be dynamically increased at run time, while the disadvantage is that memory blocks are uncertain during creation and may fail.
Heap memory pool mode creates a memory pool from global heap memory. When an Allocator class object is created, use the new operator to create a memory pool. Then use the memory blocks in the memory pool for memory allocation.
The static memory pool mode uses memory pools allocated from static memory. Static memory pools are allocated by consumers rather than created by Allocator objects.
Heap memory pool mode and static memory pool mode provide continuous use of memory operations because the memory allocator does not need to allocate separate memory blocks. The process of allocating memory in this way is very fast and deterministic.
Class design
The interface of the class is simple. Allocate () returns a pointer to a block of memory, and Deallocate () frees up memory for reuse. The constructor needs to set the size of the object, and if you use a memory pool, you need to allocate memory pool space.
The parameters in the constructor of the class are used to determine the location of the memory block allocation. The size parameter controls the size of the fixed memory block. The objects parameter sets the number of requested memory blocks. A value of 0 indicates that new memory blocks are requested from heap memory, and a value other than 0 means that object instance space is allocated using a memory pool (heap memory pool or static memory pool). The memory parameter is a pointer to static memory. If memory equals 0 and objects is non-zero, Allocator creates a memory pool from heap memory. The static memory pool memory size must be size*object bytes. The name parameter is named for the memory allocator and is used to collect allocator usage information.
Class Allocator {public: Allocator (size_t size, UINT objects=0, CHAR* memory=NULL, const CHAR* name=NULL);...
The following example shows how constructors in three allocator modes are assigned.
/ / Heap blocks mode with unlimited 100 byte blocksAllocator allocatorHeapBlocks (100); / / Heap pool mode with 20,100 byte blocksAllocator allocatorHeapPool (100,20); / / Static pool mode with 20,100 byte blockschar staticMemoryPool [100 * 20]; Allocator allocatorStaticPool (100,20, staticMemoryPool)
To simplify the static memory pool method, the AllocatorPool template class is provided. The * * parameter of the template sets the application memory object type, and the second parameter sets the number of application objects.
/ / Static pool mode with 20 MyClass sized blocks AllocatorPool allocatorStaticPool2
Deallocate () puts the memory address on the "stack". This "stack" is implemented like a single item linked list ("release list"), but only objects with headers can be added or removed, and their behavior is similar to that of a stack. Using "stack" makes allocating and releasing operations faster, because there is no need for full linked list traversal, but only press and pop operations.
Void* memory1 = allocatorHeapBlocks.Allocate
This links the memory block in the release list without adding additional storage. For example, when we use a global operate new, we first request memory and then call the constructor. The process of delete is the opposite, first calling the destructor and then freeing up memory. After the destructor is called, the memory is no longer used by the original object before it is freed to the heap, but is placed in the "release list" for reuse. Since the Allocator class needs to save the block of memory that has been freed, when using the delete operator, we point the next pointer in the "release list" to the memory address of the object being delete. When the application uses this block of memory again, the pointer is overwritten to the address of the object. In this way, there is no need to pre-instantiate memory space.
Connecting memory blocks together using the memory of a freed object means that the memory space of the object needs to be large enough to accommodate the amount of memory occupied by a pointer. The code in the constructor initialization list ensures that the minimum block size is no less than the size of the block occupied by the pointer.
The destructor of the class frees memory by releasing the heap memory pool or traversing the "release list" and releasing memory blocks one by one. Because Allocator class objects are often used as static objects, Allocator objects are released at the end of the program. For most embedded devices, the application ends only when people unplug it. Therefore, for this kind of embedded device, the role of the destructor does not matter.
If heap memory block mode is used, blocks allocated at the end of the application cannot be freed unless all allocated memory is linked to the release list. Therefore, all objects should be "deleted" at the end of the program (meaning put in the "release list"). This seems to be a memory leak and raises an interesting problem. Should Allocator track blocks of memory that are in use and have been freed? The answer is no. It is assumed that once a piece of memory is used by the application through the pointer, the application has the responsibility to return the block pointer to Allocator by calling Deallocate () before the program ends. In that case, we only need to track the blocks of memory released.
The use of code
Allocator is easy to use, so create macros to automatically implement the interface in the client class. The macro provides a statically typed instance of Allocator and two member functions: operator new and operator delete. By overriding the new and delete operators, Allocator intercepts and handles the memory allocation behavior of all client classes.
The DECLARE_ alter macro provides a header file interface and should be included when the class is defined, as follows:
# include "Allocator.h" class MyClass {DECLARE_ALLOCATOR// remaining class definition}
The operator new function calls the memory space required by Allocator to create an instance of the class. After the memory is allocated, by definition, the operator new calls the constructor of the class. The rewritten new only modifies the task of allocating memory. The call to the constructor is guaranteed by the language. When you delete an object, the system first calls the destructor and then the execution operator delete function. The operator delete uses the Deallocate () function to add a block of memory to the release list.
Although not explicitly stated, the operator delete is a static function (static functions can only call static members). Therefore, it cannot be declared as virtual. It seems that deleting the object through the pointer of the base class does not achieve the purpose of deleting the real object. After all, a static function that calls a base class pointer only calls the member function of the base class, not the member function of its real type. However, we know that the destructor is called first when the operator delete is called. The destructor decorated as virtual actually calls the destructor of the subclass. After the class's destructor is executed, the subclass's operator delete function is called. So in fact, because of the call to the virtual destructor, the overridden operator delete is called in the subclass. Therefore, when you delete an object using a base class pointer, the destructor of the base class object must be declared as virtual. Otherwise, the destructor and operator delete will not be called correctly.
The IMPLEMENT_ allocation macro is the source file implementation part of the interface and should be placed in the source file.
IMPLEMENT_ALLOCATOR (MyClass, 0,0)
After using the macros above, you can create and destroy instances of the class as below, and colleagues recycle the free memory space.
MyClass* myClass = new MyClass (); delete myClass
The Allocator class supports single inheritance and multiple inheritance. For example, the Derived class inherits the Base class, and the following code is correct.
Base* base = new Derived;delete base; runtime
At run time, there are no reusable memory blocks in the release list when Allocator initializes. Therefore, * calls to Allocate () will get memory space from the memory pool or heap. With the execution of the program, the continuous use of objects in the system will cause fluctuations in the allocator. And new memory will be requested and created only if memory is not available in the release list. Eventually, the instance of the object used by the system will be fixed, so each memory allocation will use the existing memory space instead of requesting it from the memory pool or heap.
The Allocator allocator is more efficient than allocating all object memory using the memory manager. When memory is allocated, the memory pointer simply pops up from the "release list", which is very fast. When the memory is released, it only puts the memory pointer into the "release list", which is also very fast.
Benchmark test
Comparative performance tests using Allocator and global heap memory on Windows PC show the high performance of Allocator. Test allocates and frees 20000 4096 and 2048 memory blocks to test the speed at which memory is allocated and freed. For details of the algorithm tested, see the code in the attachment.
AllocatorModeRunBenchmark Time (mS) Global HeapDebug Heap11640Global HeapDebug Heap21864Global HeapDebug Heap31855Global HeapRelease Heap155Global HeapRelease Heap247Global HeapRelease Heap347AllocatorStatic Pool119AllocatorStatic Pool27AllocatorStatic Pool37AllocatorHeap Blocks130AllocatorHeap Blocks27AllocatorHeap Blocks37
When executing in debug mode, Windows uses debug heap memory. Adding additional security checks to debug heap memory degrades performance. Publishing heap memory performance is better because security checks are not used. Disable debugging memory mode by setting _ NO_DEBUG_HEAP=1 in "debugging"-"Environment" in the Visual Studio project option.
The global debug heap memory mode takes an average of 1.8 seconds, which is the slowest. Release on memory mode about 50 milliseconds, slightly faster. The benchmark scenario is very simple. In practice, different sizes of memory blocks and random requests and releases may produce different results. However, the simplest is also the most telling. The memory manager is slower than the Allocator memory allocator and largely depends on the implementation capabilities of the platform.
The memory allocator Allocator uses a static memory mode that does not depend on heap memory allocation. Once the release list contains a memory block, its execution time is about 7 milliseconds. It takes 19 milliseconds to prevent the memory in the memory pool from being managed in the Allocator allocator.
When Aloocator uses heap memory mode, when there is reusable memory in the release list, it is as fast as static memory mode. The heap memory mode relies on the global heap to obtain memory blocks, but recycles the memory in the release list. * it takes 30 milliseconds to apply for heap memory. Due to the reuse of memory in the release list, subsequent requests take only 7 milliseconds.
The above benchmark results show that the Allocator memory allocator is more efficient, with seven times the speed of the Windows global release heap memory mode.
For embedded systems, I use Keil to run the same test on ARM STM32F4 CPU (168Hz). Due to resource constraints, I reduced the number of memory blocks to 500 and the single memory block size to 32 and 16 bytes. Here are the results:
AllocatorModeRunBenchmark Time (mS) Global HeapRelease111.6Global HeapRelease211.6Global HeapRelease311.6AllocatorStatic Pool10.85AllocatorStatic Pool20.79AllocatorStatic Pool30.79AllocatorHeap Blocks11.19AllocatorHeap Blocks20.79AllocatorHeap Blocks30.79
ARM-based benchmarks show that classes using the Allocator allocator perform 15 times faster. This result dwarfs the performance of Keil heap memory. The benchmark allocates 500 16-byte blocks of memory for testing. 500 32-byte blocks of memory are requested after each 16-byte memory is deleted. Global heap memory takes 11.6 milliseconds, and after memory fragmentation, the memory manager may be more time-consuming without a security check.
Distributor resolution
The decision is whether you need to use an allocator. If your project doesn't care about the speed of execution and the need for fault tolerance, then you may not need a custom allocator, and the global heap allocation manager is sufficient.
On the other hand, if you need to consider execution speed and fault-tolerant management, the allocator will play a role. You need to choose the mode of the allocator according to the needs of the project. The design of mission-critical systems may force the use of global heap memory. Dynamic memory allocation may be more efficient and elegant. In this case, you can use heap memory mode to obtain memory usage parameters during debugging and development, and then switch to static memory pool mode at release time to avoid performance consumption caused by memory allocation. Some compile-time macros can be used to switch modes.
In addition, the heap memory mode may be more suitable for applications. This mode uses the heap to get new memory while preventing heap fragmentation errors. When the "release list" links enough memory blocks, it can speed up the efficiency of memory allocation.
Issues involving multithreading that are not implemented in the source code are beyond the scope of this article. After running the system for a while, you can easily use the GetlockCount function and the GetName function to get the number and name of memory blocks. These metric parameters provide information about memory allocation. Apply for as much memory as possible to give the allocator some flexibility to avoid running out of memory.
Debug memory leaks
Debugging memory leaks is difficult because heap memory is like a black box, invisible to the type and size of allocated objects. With Allocator, memory leak checking becomes a little easier because Allocator tracks the total number of memory blocks. Repeating the output (e. G. to the terminal) of GetBlockCount and GetName for each allocator instance and comparing their differences gives us a better understanding of how the allocator allocates memory.
Error handling
The new_handler function is used in C++ to handle memory allocation errors. If the memory manager makes an error while requesting memory, the user's error handler will be called. By copying the user's error handler address to new_handler, the memory manager can call the user-defined error handler. In order to keep the error handling mechanism of the Allocator class consistent with that of the memory manager, the allocator also calls the error handling function through new_handler to centrally handle all memory allocation errors.
Static void out_of_memory () {/ / new-handler function called by Allocator when pool is out of memoryassert (0);} int _ tmain (int argc, _ TCHAR* argv []) {std::set_new_handler (out_of_memory); Limit
The allocator class does not support memory allocation for array objects. Creating separate memory for each object is not guaranteed because multiple calls to new do not guarantee the continuity of memory blocks, but this is required by the array. Therefore, Allocator only supports the allocation of fixed-size memory blocks, not object arrays.
Transplant problem
Allocator calls the function pointed to by new_handle when the static memory pool is exhausted, which is not appropriate for some systems. Assuming that the new_handle function does not return, such as endless loops or assertions, calling this function has no effect. This doesn't help when using fixed memory pools.
Thank you for your reading, the above is the content of "what are the knowledge points related to C++ fixed memory block allocator". After the study of this article, I believe you have a deeper understanding of what knowledge points related to C++ fixed memory block allocator, and the specific use still needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.