C++ 的 allocator 机制

介绍 C++ 的 allocator 机制

allocator 的实现

默认的 allocator 的实现如下所示。可以发现这是基于 malloc 的。

因此,诸如 jemalloc 的内存管理机制对 C++ 同样是有作用的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <cstdlib> // for std::malloc and std::free
#include <new> // for std::bad_alloc

namespace std {

template <typename T>
class allocator {
public:
using value_type = T;

allocator() noexcept {}

template <typename U>
allocator(const allocator<U>&) noexcept {}

T* allocate(std::size_t n) {
if (n > std::size_t(-1) / sizeof(T))
throw std::bad_alloc();
if (auto p = static_cast<T*>(std::malloc(n * sizeof(T))))
return p;
throw std::bad_alloc();
}

void deallocate(T* p, std::size_t) noexcept {
std::free(p);
}
};

template <typename T, typename U>
bool operator==(const allocator<T>&, const allocator<U>&) noexcept {
return true;
}

template <typename T, typename U>
bool operator!=(const allocator<T>&, const allocator<U>&) noexcept {
return false;
}

} // namespace std

std::pmr

Polymorphic Memory Resources 解决了什么问题?

  1. 简化 custom allocator 的实现
    主要提供了 std::pmr::memory_resource 和 std::pmr::polymorphic_allocator。
  2. 动态类型
    std::pmr::polymorphic_allocator 允许动态修改 allocator 的策略,而不需要修改 container 的代码。
  3. 提供了一些预定义的策略
    std::pmr::monotonic_buffer_resource 是最简单的分配器。
    std::pmr::unsynchronized_pool_resource 是基于 pool 的分配器。
  4. 可以跨多个 container 和 components 统一管理内存分配方案

如下所示,需要用 std::pmr 下面的 container

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <memory_resource>
#include <vector>
#include <iostream>

int main() {
// Create a buffer for monotonic_buffer_resource
char buffer[1024];
std::pmr::monotonic_buffer_resource pool{buffer, sizeof(buffer)};

// Create a vector using the custom memory resource
std::pmr::vector<int> vec{&pool};

// Use the vector as usual
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
return 0;
}

Memory Trace 的讨论

operator new 和自定义 allocator 的区别

In C++, both custom allocators and custom operator new play roles in managing memory allocation, but they serve different purposes and operate at different levels of abstraction.

Custom Allocator:

A custom allocator typically refers to a class that provides an interface for allocating and deallocating memory.
It’s a more general-purpose mechanism that can be used not only for dynamic memory allocation but also for other types of memory management, like managing memory pools or implementing specialized allocation strategies.
Custom allocators are often used with standard library containers like std::vector, std::map, etc., allowing you to customize how memory is allocated and deallocated for these containers.
Custom allocators are used through allocators traits like std::allocator_traits and can be customized for specific needs.

Custom operator new:
operator new is a language-level function used for dynamic memory allocation in C++. It’s responsible for allocating memory for objects.
Customizing operator new typically involves overloading it or providing a replacement function. This allows you to intercept and customize memory allocation behavior for individual types or globally.
This approach is more specific and low-level compared to custom allocators. It directly affects how memory is allocated for individual objects.
Custom operator new is often used when you need to apply specific allocation strategies or track memory usage at the level of individual objects rather than at the container level.
In summary, custom allocators offer a more flexible and higher-level approach to memory management, suitable for container classes and general-purpose memory management, while custom operator new provides a more direct and low-level means of customizing memory allocation behavior for individual objects.