C++内存管理变革(4):boost::object_pool与gc allocator

2024-01-18 04:32

本文主要是介绍C++内存管理变革(4):boost::object_pool与gc allocator,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

C++内存管理变革(4): boost::object_pool

许式伟 (版权声明)
2007-4-21

这篇文章拖的有点久了。NeutralEvil 在3个月之前就在催促我继续写了。只是出于WinxGui完整性的考虑,我一直在刻意优先去补充其它方面的文章,而不是让人去误会WinxGui是一个内存管理库了。:)

言归正传。我们在内存池(MemPool)技术详解已经介绍了boost::pool组件。从内存管理观念的变革来看,这是是一个传统的MemPool组件,尽管也有一定的改进(但只是性能上的改进)。但boost::object_pool不同,它与我在C++内存管理变革强调的观念非常吻合。可以认为,boost::object_pool是一种不通用的gc allocator组件。

我已经多次提出gc allocator的概念。这里仍然需要强调一下,所谓gc allocator,是指具垃圾回收能力的allocatorC++内存管理变革(1) 中我们引入了这个概念,但是没有明确gc allocator一词。

boost::object_pool内存管理观念

boost::object_pool的了不起之处在于,这是C++从库的层次上头一次承认,程序员在内存管理上是会犯错误的,由程序员来确保内存不泄漏是困难的。boost::object_pool允许你忘记释放内存。我们来看一个例子:

    class X { … };
 
    
void func()
    {
        boost::object_pool
<X> alloc;
 

        X* obj1 = alloc.construct();
        X
* obj2 = alloc.construct();
        alloc.destroy(obj2);
    }

如果boost::object_pool只是一个普通的allocator,那么这段代码显然存在问题,因为obj1的析构函数没有执行,申请的内存也没有释放。

但是这段代码是完全正常的。是的,obj1的析构确实执行了,所申请内存也被释放了。这就是说,boost::object_pool既支持你手工释放内存(通过主动调用object_pool::destroy),也支持内存的自动回收(通过object_pool::~object_pool析构的执行)。这正符合gc allocator的规格。

注:内存管理更好的说法是对象管理。内存的申请和释放更确切的说是对象的创建和销毁。但是这里我们不刻意区分这两者的差异。

boost::object_pool与AutoFreeAlloc

我们知道,AutoFreeAlloc不支持手工释放,而只能等到AutoFreeAlloc对象析构的时候一次性全部释放内存。那么,是否可以认为boost::object_pool是否比AutoFreeAlloc更加完备呢?

其实不然。boost::object_pool与AutoFreeAlloc都不是完整意义上的gc allocator。AutoFreeAlloc因为它只能一次性释放,故此仅仅适用特定的用况。然而尽管AutoFreeAlloc不是普适的,但它是通用型的gc allocator。而boost::object_pool只能管理一种对象,并不是通用型的allocator,局限性其实更强。

boost::object_pool的实现细节

大家对boost::object_pool应该已经有了一个总体的把握。现在,让我们深入到object_pool的实现细节中去。

内存池(MemPool)技术详解中,我们介绍boost::pool组件时,特意提醒大家留意pool::ordered_malloc/ordered_free函数。事实上,boost::object_poolmalloc/construct, free/destroy函数调用了pool::ordered_malloc, ordered_free函数,而不是pool::malloc, free函数。

让我们解释下为什么。

其实这其中的关键,在于object_pool要支持手工释放内存和自动回收内存(并自动执行析构函数)两种模式。如果没有自动析构,那么普通的MemPool就足够了,也就不需要ordered_free。既然有自动回收,同时又存在手工释放,那么就需要区分内存块(MemBlock)中哪些结点(Node)是自由内存结点(FreeNode),哪些结点是已经使用的。对于哪些已经是自由内存的结点,显然不能再调用对象的析构函数。

我们来看看object_pool::~object_pool函数的实现:

template <typename T, typename UserAllocator>
object_pool
<T, UserAllocator>::~object_pool()
{
  
// handle trivial case
  if (!this->list.valid())
    
return;
 
  details::PODptr
<size_type> iter = this->list;
  details::PODptr
<size_type> next = iter;
 
  
// Start ’freed_iter’ at beginning of free list
  void * freed_iter = this->first;
 
  
const size_type partition_size = this->alloc_size();
 
  
do
  {
    
// increment next
    next = next.next();
  
    
// delete all contained objects that aren’t freed
  
    
// Iterate ’i' through all chunks in the memory block
    for (char * i = iter.begin(); i != iter.end(); i += partition_size)
    {
      
// If this chunk is free
      if (i == freed_iter)
      {
        
// Increment freed_iter to point to next in free list
        freed_iter = nextof(freed_iter);
 
        
// Continue searching chunks in the memory block
        continue;
      }
  
      
// This chunk is not free (allocated), so call its destructor
      static_cast<*>(static_cast<void *>(i))->~T();
      
// and continue searching chunks in the memory block
    }
  
    
// free storage
    UserAllocator::free(iter.begin());
  
    
// increment iter
    iter = next;
  } 
while (iter.valid());
  
  
// Make the block list empty so that the inherited destructor doesn’t try to
  
//  free it again.
  this->list.invalidate();
}

这段代码不难理解,object_pool遍历所有申请的内存块(MemBlock),并遍历其中所有结点(Node),如果该结点不出现在自由内存结点(FreeNode)的列表(FreeNodeList)中,那么,它就是用户未主动释放的结点,需要进行相应的析构操作。

现在你明白了,ordered_malloc是为了让MemBlockList中的MemBlock有序,ordered_free是为了让FreeNodeList中的所有FreeNode有序。而MemBlockList, FreeNodeList有序,是为了更快地检测Node是自由的还是被使用的(这实际上是一个集合求交的流程,建议你看看std::set_intersection,它定义在STL的 中)。

C++内存管理变革-系列文章 

  • C++内存管理变革
  • C++内存管理变革(2):最袖珍的垃圾回收器
  • C++内存管理变革(3):另类内存管理
  • 再论C++之垃圾回收(GC)

点击这里查看更多内存管理相关文章。




这篇关于C++内存管理变革(4):boost::object_pool与gc allocator的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/618073

相关文章

大数据小内存排序问题如何巧妙解决

《大数据小内存排序问题如何巧妙解决》文章介绍了大数据小内存排序的三种方法:数据库排序、分治法和位图法,数据库排序简单但速度慢,对设备要求高;分治法高效但实现复杂;位图法可读性差,但存储空间受限... 目录三种方法:方法概要数据库排序(http://www.chinasem.cn对数据库设备要求较高)分治法(常

Redis多种内存淘汰策略及配置技巧分享

《Redis多种内存淘汰策略及配置技巧分享》本文介绍了Redis内存满时的淘汰机制,包括内存淘汰机制的概念,Redis提供的8种淘汰策略(如noeviction、volatile-lru等)及其适用场... 目录前言一、什么是 Redis 的内存淘汰机制?二、Redis 内存淘汰策略1. pythonnoe

Java内存泄漏问题的排查、优化与最佳实践

《Java内存泄漏问题的排查、优化与最佳实践》在Java开发中,内存泄漏是一个常见且令人头疼的问题,内存泄漏指的是程序在运行过程中,已经不再使用的对象没有被及时释放,从而导致内存占用不断增加,最终... 目录引言1. 什么是内存泄漏?常见的内存泄漏情况2. 如何排查 Java 中的内存泄漏?2.1 使用 J

高效管理你的Linux系统: Debian操作系统常用命令指南

《高效管理你的Linux系统:Debian操作系统常用命令指南》在Debian操作系统中,了解和掌握常用命令对于提高工作效率和系统管理至关重要,本文将详细介绍Debian的常用命令,帮助读者更好地使... Debian是一个流行的linux发行版,它以其稳定性、强大的软件包管理和丰富的社区资源而闻名。在使用

C++中实现调试日志输出

《C++中实现调试日志输出》在C++编程中,调试日志对于定位问题和优化代码至关重要,本文将介绍几种常用的调试日志输出方法,并教你如何在日志中添加时间戳,希望对大家有所帮助... 目录1. 使用 #ifdef _DEBUG 宏2. 加入时间戳:精确到毫秒3.Windows 和 MFC 中的调试日志方法MFC

深入理解C++ 空类大小

《深入理解C++空类大小》本文主要介绍了C++空类大小,规定空类大小为1字节,主要是为了保证对象的唯一性和可区分性,满足数组元素地址连续的要求,下面就来了解一下... 目录1. 保证对象的唯一性和可区分性2. 满足数组元素地址连续的要求3. 与C++的对象模型和内存管理机制相适配查看类对象内存在C++中,规

关于Java内存访问重排序的研究

《关于Java内存访问重排序的研究》文章主要介绍了重排序现象及其在多线程编程中的影响,包括内存可见性问题和Java内存模型中对重排序的规则... 目录什么是重排序重排序图解重排序实验as-if-serial语义内存访问重排序与内存可见性内存访问重排序与Java内存模型重排序示意表内存屏障内存屏障示意表Int

SpringBoot使用minio进行文件管理的流程步骤

《SpringBoot使用minio进行文件管理的流程步骤》MinIO是一个高性能的对象存储系统,兼容AmazonS3API,该软件设计用于处理非结构化数据,如图片、视频、日志文件以及备份数据等,本文... 目录一、拉取minio镜像二、创建配置文件和上传文件的目录三、启动容器四、浏览器登录 minio五、

如何测试计算机的内存是否存在问题? 判断电脑内存故障的多种方法

《如何测试计算机的内存是否存在问题?判断电脑内存故障的多种方法》内存是电脑中非常重要的组件之一,如果内存出现故障,可能会导致电脑出现各种问题,如蓝屏、死机、程序崩溃等,如何判断内存是否出现故障呢?下... 如果你的电脑是崩溃、冻结还是不稳定,那么它的内存可能有问题。要进行检查,你可以使用Windows 11

在 VSCode 中配置 C++ 开发环境的详细教程

《在VSCode中配置C++开发环境的详细教程》本文详细介绍了如何在VisualStudioCode(VSCode)中配置C++开发环境,包括安装必要的工具、配置编译器、设置调试环境等步骤,通... 目录如何在 VSCode 中配置 C++ 开发环境:详细教程1. 什么是 VSCode?2. 安装 VSCo