首页 > 编程知识 正文

list底层实现原理,数据库连接池的优点

时间:2023-05-04 02:54:27 阅读:162973 作者:4182

对象池是一种预先初始化一组可重用实体的设计模式,而不是根据需要销毁和重建。 使用套接字描述符时,通常进行池化。 实际上,套接字描述符的数量通常很少,最多为几千个。 采用池方式是因为初始化成本非常高。 在最近发表的一篇博文中,ClojureWerkz的核心成员bldfj Petrov研究了另一个连接池应用场景。 也就是说,大量进行生存期短、初始化成本低的对象池化,降低内存分配和再分配成本,避免内存碎片。

bldfj认为目标池是减少GC压力的首选方法,也是最简单的方法。 在以下两种分配模式下,可以选择使用对象池:

对象以一定的速度不断分配,垃圾收集时间逐渐增加,内存使用率增大;

对象分配具有爆发期,每次爆发都会导致系统延迟,并伴有明显的GC中断。

在大多数情况下,这些对象是用作APP应用程序与内部消息总线、通信层或某些API之间的信封的数据容器或数据封装器。 这很常见。 例如,数据库驱动程序为每个请求和响应创建Request和Response对象,而消息系统使用消息和事件包装器。 对象池有助于存储和重用这些生成的对象实例。

bldfj介绍了两种基本的对象池回收模型:“借用”和“引用计数”。 前者更清晰,而后者意味着实现自动回收。

非常类似于垃圾回收运行时上面的malloc/free。 当然,使用这种方式时,开发者需要面对以前使用垃圾收集以外的语言时遇到的问题。 如果对象已释放并返回池,则更改或读取会产生意外的结果。 例如,在c语言中,对释放的指针执行任何操作都会导致块错误。 借用具有明确开始/结束点的操作。 在大多数情况下,如果对象可以由多个线程同时访问,请不要使用。 借用的最大优点是不知道对象池的存在。 被借用的客体本身需要某种reset机制,借用和归还的操作都由目标消费者进行。

引用计数在实现方面稍显复杂,但对数据结构有更好的控制。 通过将对象池封装在函数接口中,消费者不需要知道它,如下所示

(pooledObject,pooledobjectconsumer(-{

pooledObject.retain (;

pooledobjectconsumer.accept (pooled object;

pooledObject.release (;

(;

每次对象进入上面的代码块时,调用方都会返回对象,并在执行块完成后释放。 每个对象都有内部计数器和对池的引用。 如果计数器为0,则对象返回池。

通常,引用计数用于多个消费者访问同时分配的对象,只有在所有消费者释放对象引用时才能回收对象。 此方式也适用于管道和嵌套处理。 在这种情况下,开发人员可以避免显式的开始/结束操作。

分配负责在池中的对象不足时分配新资源。 bldfj介绍了三种分配触发方法:

空池触发器:随时只要池为空,就分配对象。 这是最简单的方法。

水位线:触发空池的缺点是,一个对象请求会因执行对象分配而中断。 为了避免这种情况,请使用水位线触发器。 当从池中请求新对象时,检查池中可用的对象数量。 如果可用对象小于阈值,则启动分配进程。

Lease/Return速度:水位线触发在大多数情况下是足够的,但在某些情况下可能需要更高的精度。 在这种情况下,可以使用lease和return速度。 例如,如果池中有100个对象,每秒获取20个对象,但只返回10个对象,则9秒后池将为空。 开发人员可以使用此信息提前规划对象分配。

发展策略用于指定触发分配进程后必须分配的对象数量。 bldfj也介绍了三种方法:

固定大小—这是最简单的映像池实现方法。 对象一次预分配,并且对象池随后不再增长。 此实现适用于对象数量相对固定的情况,但固定的池大小可能会导致资源不足。

小规模增长:允许连接池小规模增长,例如一次分配一个对象以避免资源饥饿。

增加块:如果无法接受因分配导致的中断,则必须确保池中随时有可用对象。 在这种情况下,必须使用阻滞生长。 例如,水位线每达到25%,将对象池扩大25%。 但是,这种方式容易导致存储器溢出。 与Lease/Return速度分配触发策略结合使用,可以更准确地确定池大小。

当然,使用对象池意味着开发人员将开始自己管理内存,因此必须注意以下事项。

引用遗漏:对象已在系统中的某个位置注册,但未返回池。

早期回收:如果消费者决定将对象返回到对象池,但仍具有该引用,并且正在尝试执行写入或读取操作,则会发生这种情况。

隐式回收:如果使用引用计数,则可能会出现这种情况。

大小错误:这在使用字节缓冲区和数组时很常见。 对象的大小必须不同,并且是以自定义方式构建的,但是在返回到对象池后,它们将作为公共对象重用。

重复订购:这是一个引用遗漏变种,在存在复用的情况下特别容易发生。 一个对象被分配到多个位置,但其中一个对象正在释放该对象。

就地修改 :对象不可变是最好的,但如果不具备那样做的条件,就可能在读取对象内容时遇到内容被修改的问题。

缩小对象池 :当池中有大量的未使用对象时,要缩小对象池。

对象重新初始化 :确保每次从池中取得的对象不含有上次使用时留下的脏字段。

最后,bldfj指出:

对象池并不适合所有人。在应用程序开发的早期阶段就开始使用对象池是没有意义的,因为你那时候还不能确切地知道什么需要池化,也不确定如何池化。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。