深度剖析C++对象池自动回收技术实现
http://www.tuicool.com/articles/mQBfQfN
对象池可以显著提高性能,如果一个对象的创建非常耗时或非常昂贵,频繁去创建的话会非常低效。对象池通过对象复用的方式来避免重复创建对象,它会事先创建一定数量的对象放到池中,当用户需要创建对象的时候,直接从对象池中获取即可,用完对象之后再放回到对象池中,以便复用。这种方式避免了重复创建耗时或耗资源的大对象,大幅提高了程序性能。本文将探讨对象池的技术特性以及源码实现。

对象池类图
- ObjectPool:管理对象实例的pool。
- Client:使用者。
适用性:
- 类的实例可重用。
- 类的实例化过程开销较大。
- 类的实例化的频率较高。
效果:
- 节省了创建类实例的开销。
- 节省了创建类实例的时间。
- 存储空间随着对象的增多而增大。
问题
目前纵观主流语言的实现方式无外乎3个步骤:
- 初始创建一定数量的对象池(也允许从外面添加对象)。
- 从对象池中取对象来使用。
- 用完之后返回对象池。
一般情况下这样是OK的,可能存在的问题是在第三步,有两个问题:
- 不方便,每次都需要显式回收对象。
- 忘记将对象放回对象池,造成资源浪费。
改进动机
解决显式回收的问题,实现自动回收,省心省力。改进之后的对象池无须提供release方法,对象会自动回收,改进之后的类图如下。

技术内幕
借助c++11智能指针,因为智能指针可以自定义删除器,在智能指针释放的时候会调用删除器,在删除器中我们将用完的对象重新放回对象池。思路比较简单,但实现的时候需要考虑两个问题:
- 什么时候定义删除器?
- 用shared_ptr还是unique_ptr?
1. 什么时候定义删除器
自定义删除器只做一件事,就是将对象重新放入对象池。如果对象池初始化的时候就自定义删除器的话,删除器中的逻辑是将对象放回对象池,放回的时候无法再定义一个这样的删除器,所以这种做法行不通。需要注意,回收的对象只能是默认删除器的。除了前述原因之外,另外一个原因是对象池释放的时候需要释放所有的智能指针,释放的时候如果存在自定义删除器将会导致对象无法删除。只有在get的时候定义删除器才行,但是初始创建或加入的智能指针是默认删除器,所以我们需要把智能指针的默认删除器改为自定义删除器。
1.2 用shared_ptr还是unique_ptr
因为我们需要把智能指针的默认删除器改为自定义删除器,用shared_ptr会很不方便,因为你无法直接将shared_ptr的删除器修改为自定义删除器,虽然你可以通过重新创建一个新对象,把原对象拷贝过来的做法来实现,但是这样做效率比较低。而unique_ptr由于是独占语义,提供了一种简便的方法方法可以实现修改删除器,所以用unique_ptr是最适合的。
1.3 实现源码
#pragma once
#include <memory>
#include <vector>
#include <functional>
template <class T>
class SimpleObjectPool
{
public:
using DeleterType = std::function<void(T*)>;
void add(std::unique_ptr<T> t)
{
pool_.push_back(std::move(t));
}
std::unique_ptr<T, DeleterType> get()
{
if (pool_.empty())
{
throw std::logic_error("no more object");
}
//every time add custom deleter for default unique_ptr
std::unique_ptr<T, DeleterType> ptr(pool_.back().release(), [this](T* t)
{
pool_.push_back(std::unique_ptr<T>(t));
});
pool_.pop_back();
return std::move(ptr);
}
bool empty() const
{
return pool_.empty();
}
size_t size() const
{
return pool_.size();
}
private:
std::vector<std::unique_ptr<T>> pool_;
};
//test code
void test_object_pool()
{
SimpleObjectPool<A> p;
p.add(std::unique_ptr<A>(new A()));
p.add(std::unique_ptr<A>(new A()));
{
auto t = p.get();
p.get();
}
{
p.get();
p.get();
}
std::cout << p.size() << std::endl;
}
如果你坚持用shared_ptr,那么回收的时候你需要这样写:
std::shared_ptr<T> get()
{
if (pool_.empty())
{
throw std::logic_error("no more object");
} std::shared_ptr<T> ptr = pool_.back();
auto p = std::shared_ptr<T>(new T(std::move(*ptr.get())), [this](T* t)
{
pool_.push_back(std::shared_ptr<T>(t));
}); //std::unique_ptr<T, DeleterType> ptr(pool_.back().release(), [this](T* t)
//{
// pool_.push_back(std::unique_ptr<T>(t));
//}); pool_.pop_back();
return p;
}
这种方式需要每次都创建一个新对象,并且拷贝原来的对象,是一种比较低效的做法。
代码仅仅是为了展示如何实现自动回收对象,没有考虑线程安全、对象池扩容策略等细节,源码链接: object_pool
总结
凡是需要自动回收的场景下都可以使用这种方式:在获取对象的时候将默认删除器改为自定义删除器,确保它可以回收。注意,回收的智能指针使用的是默认删除器,可以确保对象池释放时能正常释放对象。同时也将获取对象和释放对象时,对象的控制权完全分离。 其他的一些应用场景:多例模式,无需手动释放,自动回收。
深度剖析C++对象池自动回收技术实现的更多相关文章
- 对象池化技术 org.apache.commons.pool
恰当地使用对象池化技术,可以有效地减少对象生成和初始化时的消耗,提高系统的运行效率.Jakarta Commons Pool组件提供了一整套用于实现对象池化的框架,以及若干种各具特色的对象池实现,可以 ...
- Objective-C类成员变量深度剖析--oc对象内存模型
目录 Non Fragile ivars 为什么Non Fragile ivars很关键 如何寻址类成员变量 真正的“如何寻址类成员变量” Non Fragile ivars布局调整 为什么Objec ...
- 超过1W字深度剖析JVM常量池(全网最详细最有深度)
面试题:String a = "ab"; String b = "a" + "b"; a == b 是否相等 面试考察点 考察目的: 考察对 ...
- java对象池化技术
https://blog.csdn.net/tiane5hao/article/details/85957840 文章目录 先写一个简单通用的对象池 通过上面的通用池实现jedis连接池 连接池测试 ...
- Java对象池技术的原理及其实现
看到一片有关于java 对象基础知识,故转载一下,同时学习一下. 摘 要 本文在分析对象池技术基本原理的基础上,给出了对象池技术的两种实现方式.还指出了使用对象池技术时所应注意的问题. 关键词 对象池 ...
- Java中的对象池技术
java中的对象池技术,是为了方便快捷地创建某些对象而出现的,当需要一个对象时,就可以从池中取一个出来(如果池中没有则创建一个),则在需要重复重复创建相等变量时节省了很多时间.对象池其实也就是一个内存 ...
- Java--对象池化技术 org.apache.commons.pool2.ObjectPool
org.apache.commons.pool2.ObjectPool提供了对象池,开发的小伙伴们可以直接使用来构建一个对象池 使用该对象池具有两个简单的步骤: 1.创建对象工厂,org.apache ...
- 设计模式之美:Object Pool(对象池)
索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):实现 DatabaseConnectionPool 类. 实现方式(二):使用对象构造方法和预分配方式实现 ObjectPool ...
- 7. SOFAJRaft源码分析—如何实现一个轻量级的对象池?
前言 我在看SOFAJRaft的源码的时候看到了使用了对象池的技术,看了一下感觉要吃透的话还是要新开一篇文章来讲,内容也比较充实,大家也可以学到之后运用到实际的项目中去. 这里我使用Recyclabl ...
随机推荐
- CSS3的文字阴影—text-shadow
text-shadow还没有出现时,大家在网页设计中阴影一般都是用photoshop做成图片,现在有了css3可以直接使用text-shadow属性来指定阴影. 这个属性可以有两个作用,产生阴影和模糊 ...
- tomcat war包部署
平常的开发我们都是通过IDE进行项目的部署,但有时候我们不得不进行手工部署(例如在Server上). 手工部署分为以下几步: 第1步: 用maven打war包 (假如得到的war包名为: appkit ...
- G面经prepare: set difference
给你A{1,2,3,4,4,5}, B{2,4},求A-B={1,3,4,5},很简单. visit 1 只用一个HashMap package TwoSets; import java.util.* ...
- 树形DP(Holiday's Accommodation HDU4118)
题意:有n间房子,之间有n-1条道路连接,每个房间里住着一个人,这n个人都想到其他房间居住,并且每个房间不能有两个人,问所有人的路径之和最大是多少? 分析:对于每条边来说,经过改边的人由该边两端元素个 ...
- 批量文本读取URL获取正常访问且保留对应IP
#coding=utf-8 import sys import requests for i in range(3000,4999,1): url = 'http://192.168.88.139:8 ...
- linux第2天 信号 wait
孤儿进程和僵尸进程 如果父进程先退出,子进程还没退出那么子进程的父进程将变为init进程.(注:任何一个进程都必须有父进程) 如果子进程先退出,父进程还没退出,那么子进程必须等到父进程捕获到了子进程的 ...
- POJ 2417 Discrete Logging(离散对数-小步大步算法)
Description Given a prime P, 2 <= P < 231, an integer B, 2 <= B < P, and an integer N, 1 ...
- paper 68 :MATLAB中取整函数(fix, floor, ceil, round)的使用
MATLAB取整函数 1)fix(x) : 截尾取整. >> fix( [3.12 -3.12]) ans = 3 -3 (2)floor(x):不超过x 的最大整数.(高 ...
- PAT乙级 1022. D进制的A+B (20)
1022. D进制的A+B (20) 时间限制 100 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 CHEN, Yue 输入两个非负10进制整数A和 ...
- Limit the query running time with Resource limit facility (RLF)
If you need to limit the query(package,plan) running time, but the JCL/JOB TIME parameters doesn't w ...