【C++内存管理】内存池

这篇具有很好参考价值的文章主要介绍了【C++内存管理】内存池。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. C++17 memory_resource 内存池

C++17 引入了一个新的内存资源抽象层 - memory_resource。其主要目的是将内存的分配和回收与具体的数据结构解耦,从而允许开发人员使用不同的内存管理策略,提高内存管理的灵活性。memory_resource 是一个基类,可以用于创建和配置具有不同内存管理行为的内存池。

1.1 memory_resource 基类

memory_resource 是一个抽象基类,提供了三个纯虚函数,需要被子类实现:

  • do_allocate(size_t bytes, size_t alignment):根据指定的字节和对齐方式分配内存。
  • do_deallocate(void* p, size_t bytes, size_t alignment):释放之前分配的内存。
  • do_is_equal(const memory_resource& other) const noexcept:判断两个内存资源是否等效。

以下是 memory_resource 基类的一个简单实现:

class MyMemoryResource : public std::pmr::memory_resource
{
protected:
    void* do_allocate(size_t bytes, size_t alignment) override
    {
        return ::operator new(bytes);
    }

    void do_deallocate(void* p, size_t bytes, size_t alignment) override
    {
        ::operator delete(p);
    }

    bool do_is_equal(const memory_resource& other) const noexcept override
    {
        return this == &other;
    }
};

1.2 内存资源的使用

要使用 memory_resource,通常需要通过它来创建一些容器。例如,我们可以用 memory_resource 创建一个 std::pmr::vector

MyMemoryResource myMemoryResource;
std::pmr::vector<int> vec(&myMemoryResource);

这样,vec 就会使用 myMemoryResource 来进行内存的分配和回收。

1.3 全局和本地内存资源

C++ 提供了两种内存资源:全局内存资源和本地内存资源。全局内存资源是通过 std::pmr::get_default_resource() 获得的,所有没有指定内存资源的 std::pmr 对象都会使用它。本地内存资源是创建 std::pmr 对象时指定的,它会覆盖全局内存资源。

例如:

std::pmr::vector<int> vec; // 使用全局内存资源
std::pmr::vector<int> vec(&myMemoryResource); // 使用本地内存资源

1.4 其他内存资源类型

除了用户自定义的内存资源,C++ 还提供了几种预定义的内存资源:

  • std::pmr::new_delete_resource():用 newdelete 进行内存分配和回收。
  • std::pmr::null_memory_resource():任何尝试分配内存的操作都会抛出 std::bad_alloc 异常。
  • std::pmr::unsynchronized_pool_resourcestd::pmr::synchronized_pool_resource:内存池资源,分别为线程不安全和线程安全版本。

下面的章节将会继续介绍这两种内存池资源。

2. struct unsynchronized_pool_resource

unsynchronized_pool_resource 是 C++17 中 std::pmr::memory_resource 的一种实现。这种内存资源管理方式以内存池的形式存在,其主要的优势在于减少内存分配和回收的开销。这种资源被设计为线程不安全,所以对于多线程环境需要开发者自己进行管理和同步。

2.1 unsynchronized_pool_resource 的基本原理

unsynchronized_pool_resource 内部维护了一系列的内存池,每个内存池负责管理一定大小范围的内存块。当需要分配内存时,内存池资源会根据请求的大小选择一个适当的内存池,并从中分配一个内存块。如果对应的内存池没有可用的内存块,那么内存池资源会从上游内存资源那里获取更多的内存。

2.2 unsynchronized_pool_resource 的使用

要使用 unsynchronized_pool_resource,需要创建一个 unsynchronized_pool_resource 对象,并将其作为其他 std::pmr 对象的内存资源。以下是一个例子:

std::pmr::unsynchronized_pool_resource pool;
std::pmr::vector<int> vec(&pool);

在这个例子中,vec 将使用 pool 来进行内存的分配和回收。

2.3 unsynchronized_pool_resource 的配置

unsynchronized_pool_resource 具有一些可配置的参数,包括最大和最小块大小、最大块数等。这些参数可以通过 std::pmr::pool_options 结构体来设置,后面的章节将详细介绍这个结构体。

2.4 unsynchronized_pool_resource 的线程安全性

如其名所示,unsynchronized_pool_resource 是线程不安全的。如果在多线程环境中使用 unsynchronized_pool_resource,则必须确保每个线程都使用自己的 unsynchronized_pool_resource,或者通过某种同步机制(如互斥锁)来保护 unsynchronized_pool_resource

虽然这增加了使用 unsynchronized_pool_resource 的复杂性,但是由于避免了同步的开销,unsynchronized_pool_resource 在某些场景下可能会比同步的内存池更高效。

2.5 unsynchronized_pool_resource 与 new/delete 的对比

相比于使用 newdelete 进行内存管理,unsynchronized_pool_resource 主要的优势在于减少了内存分配和回收的开销。特别是对于频繁分配和回收小块内存的场景,使用 unsynchronized_pool_resource 可以显著提高性能。

此外,由于 unsynchronized_pool_resource 可以回收并重用内存块,因此它还可以减少内存碎片,从而提高内存利用率。

3. pool_options 内存池配置

pool_options 是 C++17 中用于配置 unsynchronized_pool_resourcesynchronized_pool_resource 的结构体。它提供了一些参数,可以用于调整内存池的行为,以适应不同的应用场景。

3.1 pool_options 的参数

pool_options 结构体包括以下几个参数:

  • max_blocks_per_chunk:每个块中最多可以包含的块数量。这个参数用于限制内存池从上游内存资源一次性获取的内存量。如果设置为 0,则表示没有限制。

  • largest_required_pool_block:要求内存池能够处理的最大块大小。这个参数用于确定内存池应该管理哪些大小的内存块。

以下是一个例子:

std::pmr::pool_options options;
options.max_blocks_per_chunk = 1024;
options.largest_required_pool_block = 512;

3.2 pool_options 的使用

要使用 pool_options,需要在创建 unsynchronized_pool_resourcesynchronized_pool_resource 时将其作为参数传入。以下是一个例子:

std::pmr::pool_options options;
options.max_blocks_per_chunk = 1024;
options.largest_required_pool_block = 512;

std::pmr::unsynchronized_pool_resource pool(options);

在这个例子中,pool 将根据 options 中的参数来进行内存的管理。

3.3 pool_options 的影响

通过调整 pool_options 中的参数,可以影响内存池的行为,从而优化内存的使用。

例如,如果知道应用程序会频繁地分配和回收一定大小的内存块,可以通过设置 largest_required_pool_block 来让内存池专门处理这些内存块,从而提高内存的利用率。

同样,如果知道应用程序会分配大量的内存块,可以通过增加 max_blocks_per_chunk 来减少从上游内存资源获取内存的次数,从而减少内存管理的开销。

总的来说,pool_options 提供了一种灵活的方式来配置内存池,使其能够根据应用程序的需求提供最优的内存管理策略。

4. memory_resource

我们已经在第一部分对 memory_resource 做了基础的介绍。memory_resource 是 C++17 中提供的一种内存资源抽象,它是所有内存资源的基类。在这一部分,我们将深入探讨 memory_resource 的几个关键的方法。

4.1 memory_resource::allocate()

这个方法是用户用来从 memory_resource 请求内存的接口。它接收两个参数:需要的字节数和对齐要求。然后,它将调用 do_allocate 方法来分配内存。

4.2 memory_resource::deallocate()

这个方法用于将之前分配的内存归还给 memory_resource。它接收三个参数:要归还的内存指针、该内存块的大小和对齐要求。然后,它将调用 do_deallocate 方法来归还内存。

4.3 memory_resource::is_equal()

这个方法用于判断两个 memory_resource 是否相等。在默认情况下,两个 memory_resource 是相等的,当且仅当它们是同一个对象。

4.4 memory_resource::do_allocate(), memory_resource::do_deallocate(), memory_resource::do_is_equal()

这三个方法是 memory_resource 的纯虚函数,必须由子类实现。具体的内存分配和回收行为,以及两个内存资源的比较方式,取决于这三个方法的实现。

例如,unsynchronized_pool_resourcesynchronized_pool_resource 就是通过这三个方法实现了自己的内存管理策略。

4.5 memory_resource 的使用方式

memory_resource 通常不直接使用,而是作为创建其他 std::pmr 对象时的参数,例如 std::pmr::vectorstd::pmr::string

以下是一个例子:

std::pmr::synchronized_pool_resource pool;
std::pmr::vector<int> vec(&pool);

在这个例子中,vec 将使用 pool 来进行内存的分配和回收。

memory_resource 提供了一个通用的内存资源接口,让你可以根据应用程序的需求来自定义内存管理策略。通过使用 memory_resource,你可以创建出对内存使用更有效率的数据结构。

5. unsynchronized_pool_resource 线程不安全

unsynchronized_pool_resource 是一种为 C++17 标准库提供的内存资源类,用于从预分配的内存池中进行内存分配和回收。关键的一点是,unsynchronized_pool_resource 不提供线程安全的保证。这是它与 synchronized_pool_resource 的主要区别。我们接下来详细讨论这一点。

5.1 何为线程不安全

"线程不安全"的意思是,如果同一个 unsynchronized_pool_resource 实例被多个线程同时使用,没有其他的同步机制(例如互斥锁)来保护这个实例,那么可能会导致数据竞争和未定义的行为。也就是说,多个线程不能同时对一个 unsynchronized_pool_resource 实例进行读写。

5.2 unsynchronized_pool_resource 的线程不安全

由于 unsynchronized_pool_resource 不提供内部的同步机制,所以在多线程环境中使用 unsynchronized_pool_resource 需要特别小心。以下是两种常见的使用方式:

  • 每个线程都使用自己的 unsynchronized_pool_resource 实例。这种方式的好处是,由于没有竞争,每个线程的内存操作都可以尽可能快。但是,这种方式可能会导致内存的使用不够高效,因为每个线程都需要预分配自己的内存池。

  • 使用一个共享的 unsynchronized_pool_resource 实例,并使用外部的同步机制(例如互斥锁)来保护这个实例。这种方式的好处是,可以更高效地利用内存,因为所有的线程都从同一个内存池中获取和回收内存。但是,这种方式可能会导致性能下降,因为线程需要等待其他线程释放锁。

5.3 如何处理 unsynchronized_pool_resource 的线程不安全

处理 unsynchronized_pool_resource 的线程不安全的最佳方式取决于应用程序的具体需求。如果性能是最重要的因素,并且可以接受更高的内存使用率,那么每个线程都使用自己的 unsynchronized_pool_resource 实例可能是最好的选择。如果内存效率是最重要的因素,并且可以接受更低的性能,那么使用一个共享的 unsynchronized_pool_resource 实例和外部的同步机制可能是最好的选择。

整体上,unsynchronized_pool_resource 提供了一种高效的内存管理方式,但是在多线程环境中使用时需要注意线程安全问题。通过正确地使用 unsynchronized_pool_resource,你可以根据应用程序的需求来平衡内存效率和性能。

6. synchronized_pool_resource 线程安全

synchronized_pool_resource 是 C++17 中提供的一种内存资源类,和 unsynchronized_pool_resource 类似,都是为了提供高效的内存分配和回收服务。但是,synchronized_pool_resource 的主要区别在于它是线程安全的。

6.1 什么是线程安全

在多线程编程中,如果一个函数、函数库或者类可以在多线程环境下被同时调用,而不产生错误的结果或者未定义行为,那么我们就称之为线程安全的。简单来说,线程安全就是在多线程环境下保证结果的正确性和状态的一致性。

6.2 synchronized_pool_resource 的线程安全

synchronized_pool_resource 是线程安全的,它提供了内部同步机制来确保多个线程可以同时对它进行内存分配和回收操作。也就是说,多个线程可以共享一个 synchronized_pool_resource 实例,而无需担心数据竞争或者未定义行为。

这个特性让 synchronized_pool_resource 成为在多线程环境下处理内存管理的理想选择。

6.3 如何使用 synchronized_pool_resource

使用 synchronized_pool_resource 非常简单,和 unsynchronized_pool_resource 的用法类似。你可以在创建 synchronized_pool_resource 的实例时,传入一个 pool_options 参数来配置内存池,然后使用 allocate()deallocate() 方法来分配和回收内存。

以下是一个例子:

std::pmr::pool_options options;
options.max_blocks_per_chunk = 1024;
options.largest_required_pool_block = 512;

std::pmr::synchronized_pool_resource pool(options);

// 从 pool 中分配内存
void* p = pool.allocate(100);

// 将内存归还给 pool
pool.deallocate(p, 100);

在这个例子中,即使有多个线程同时执行上述代码,synchronized_pool_resource 也能保证每次 allocate()deallocate() 操作的正确性。

synchronized_pool_resource 提供了一种线程安全、高效的内存管理方式。通过使用 synchronized_pool_resource,你可以在多线程环境下进行安全、快速的内存分配和回收,从而提高应用程序的性能。文章来源地址https://www.toymoban.com/news/detail-538712.html

到了这里,关于【C++内存管理】内存池的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++】C++内存管理,模板

    C++中通过 new 和 delete 两个操作符实现动态内存管理 为自定义类型设计,可以在申请空间得同时初始化 使用 new 先申请空间,再调用构造 使用 delete 时先调用析构,再释放空间 operator new 和 operator delete 是系统提供的全局函数,不是运算符重载, 在使用 new 或 delete 时,底层实

    2024年02月01日
    浏览(41)
  • c++ 内存管理一:初识内存分配工具

    前言 侯捷 c++内存管理学习总结笔记。 在C++中,有几种常用的内存分配工具可以帮助进行动态内存管理。 从c++应用程序自上而下,通常会有这样的几种分配内存的方式,当然最终都是直接或间接的调用系统的API。 1 new 和 delete new 和 delete :new操作符用于在堆上分配内存,de

    2024年02月11日
    浏览(44)
  • C语言 — 动态内存管理(动态内存函数)

    本期分为三篇介绍动态内存管理相关内容,关注博主了解更多 博主博客链接:https://blog.csdn.net/m0_74014525 本期介绍动态内存函数,函数如何使用、函数格式、在使用在所需要的注意点及C/C++程序的内存开辟区域 第一篇:C语言 — 动态内存管理(动态内存函数) 第二篇:C语言

    2024年02月14日
    浏览(47)
  • 「探索C语言内存:动态内存管理解析」

    🌠先赞后看,不足指正!🌠 🎈这将对我有很大的帮助!🎈 📝所属专栏:C语言知识 📝阿哇旭的主页:Awas-Home page 目录   引言 1. 静态内存 2. 动态内存 2.1 动态内存开辟函数 2.1.1 malloc函数 2.1.2 calloc函数 2.1.3 realloc函数 2.2 动态内存释放函数 2.2.1 free函数 3. 动态内存的常见

    2024年04月28日
    浏览(41)
  • 【C++初阶】七、内存管理(C/C++内存分布、C++内存管理方式、operator new / delete 函数、定位new表达式)

    ========================================================================= 相关代码gitee自取 : C语言学习日记: 加油努力 (gitee.com)  ========================================================================= 接上期 : 【C++初阶】六、类和对象(初始化列表、static成员、友元、内部类)-CSDN博客  ==================

    2024年02月05日
    浏览(45)
  • 深入理解C++内存管理

    C++的高抽象层次,又兼具高性能,是其他语言所无法替代的,C++标准保持稳定发展,更加现代化,更加强大。但在各种活跃编程语言中,C++门槛依然很高,尤其C++的内存问题(内存泄露,内存溢出,内存宕机,堆栈破坏等问题),需要理解C++标准对象模型,C++标准库,标准

    2023年04月08日
    浏览(37)
  • 【C++初阶】动态内存管理

      说明: 1. 栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的; 2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口     创建共享共享内存,做进程间通信; 3. 堆用于程序运行时动态内存分配,堆是可以上增长的;

    2024年02月06日
    浏览(40)
  • 【C++】——动态内存管理

      目录 🌞导读 🌔C/C++内存分布  🌔C++内存管理方式  🌗new/delete操作内置类型  🌗new和delete的使用方法 🌔operator new与operator delete函数 🌔new和delete的实现原理 🌗内置类型 🌗自定义类型 🌔定位new表达式  🌗使用格式 🌔malloc/free和new/delete的区别 🌟作者简介: 日出等日

    2024年02月07日
    浏览(34)
  • C++动态内存管理+模板

    💓博主个人主页:不是笨小孩👀 ⏩专栏分类:数据结构与算法👀 C++👀 刷题专栏👀 C语言👀 🚚代码仓库:笨小孩的代码库👀 ⏩社区:不是笨小孩👀 🌹欢迎大家三连关注,一起学习,一起进步!!💓 C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使

    2024年02月09日
    浏览(43)
  • C++进程、线程、内存管理

    目录 进程和线程区别 进程和线程切换的区别 系统调用流程 系统调用是否会引起线程切换 为什么需要使用虚拟内存 本质区别: 进程是资源分配的基本单元。 线程是操作系统调度的基本单元。 地址空间: 进程具有独立的虚拟地址空间。 线程共享进程的虚拟地址空间。 内存

    2024年02月10日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包