GenericObjectPool对象池化的介绍与用法

2024-06-16 18:04

本文主要是介绍GenericObjectPool对象池化的介绍与用法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

GenericObjectPool 是 Apache Commons Pool 库的一部分,它提供了一个通用的对象池实现,允许用户在需要时从池中借用和返回对象,而不是每次需要一个新实例时都创建一个。这种方法可以显著提高性能特别是对于创建开销大或需要频繁使用的对象

哪些对象创建、销毁成本较高

创建和销毁成本较大的 对象有哪些、原因是啥:

  1. 数据库连接:建立数据库连接涉及网络通讯以及数据库服务器上的资源分配和初始化。这些过程可能需要消耗显著的时间和系统资源。因此,数据库连接池(如 HikariCP、c3p0)通常用于管理这些连接,以减少每次操作所需的创建和销毁开销。

  2. 线程:创建和销毁线程涉及操作系统级的资源分配,这可能是一个高代价的操作,尤其是在需要频繁创建和销毁大量线程的场景中。Java 的 ExecutorService 提供了线程池功能,可以有效地复用线程来处理多个任务。

  3. 网络连接:建立网络连接(如 TCP 连接)涉及网络协议的握手过程,这可能导致显著的延迟和资源消耗。复用网络连接(例如通过 HTTP 连接池)可以减少这种开销。

  4. 大对象或复杂对象:创建一些内存占用大构造过程复杂的对象也可能涉及较高的开销。例如,需要加载大量数据或执行复杂初始化逻辑的对象。

  5. 外部资源(如文件或其他系统资源):操作文件或与外部系统交互(通过 API 调用等)可能涉及显著的资源开销。缓存操作结果重用外部连接可以帮助降低这种开销。

为了管理资源创建和销毁的成本,通常采用以下策略

  1. 对象池化(Pooling):如数据库连接池、线程池和 HTTP 客户端连接池,通过重用现有对象来减少创建和销毁的频率。
  2. 缓存(Caching):缓存是另一种减少资源获取成本的策略,特别是当资源获取涉及昂贵的计算或外部系统调用时。
  3. 延迟初始化(Lazy Initialization):延迟(或按需)创建资源,直到首次使用时,可以推迟资源分配的开销,并可能省略对某些永远不会使用的资源的开销。

对象创建的步骤

在Java中,创建对象时的内存分配过程主要发生在堆(Heap)内存区域。整个过程受Java虚拟机(JVM)的垃圾收集(GC)算法和内存模型的管理。这个过程可以分为以下几个步骤:

  1. 类加载检查
    当代码尝试创建一个对象实例时(如通过new XXXClassName()),JVM首先检查这个类是否已被加载、链接和初始化。如果没有,系统会执行这些步骤。

  2. 为对象分配内存
    一旦类检查完成,JVM会在堆内存中为新对象分配内存。分配的内存大小根据对象的类定义来确定,假如类有String name、Integer age、Boolean isGirl,包括所有实例变量的大小及其他一些开销(如对象头信息)。这部分内存用于存储对象的状态(即字段或属性的值)。

    内存分配方式取决于垃圾收集器的具体实现。常见的分配方式包括:
    a. 指针碰撞(Bump the Pointer):如果堆内存是整齐划分的(使用的垃圾收集算法如Minor GC后),那么对象的分配仅仅是将指向堆中的空闲区域的指针向上移动对象大小的距离
    b. 空闲列表(Free List):如果堆内存是分散的(即存在被使用和未被使用的内存区域),JVM必须通过遍历一个列表来找到足够大的空间来存放新对象。

  3. 初始化内存空间
    内存分配完毕后,JVM将分配的内存空间初始化为零值(不包括对象头),确保对象的实例变量不会有Java默认值以外的任何值对象头一般包含对象的哈希码GC代年龄等信息。

  4. 设置对象头
    JVM设置对象头信息,包括这个对象是哪个类的实例对象的哈希码和锁信息等。

  5. 执行构造方法
    最后,JVM调用对象的构造函数。构造函数方法可能会对对象变量进行初始化,这可能会覆盖在第3步中设置的零值

这一过程尽管看起来复杂,但在现代虚拟机中非常高效。JVM的设计包括了多种优化,如线程本地内存分配(TLAB),这允许每个线程在自己的内存缓冲区中分配对象,从而减少线程间的竞争

为什么使用 GenericObjectPool?

当创建一个对象的成本(时间、资源等)很高时,就可以使用对象池。对象池通过重用现有的对象而不是每次需要时都创建新对象来提高性能和资源利用率。GenericObjectPool 提供了管理这种对象池的功能,包括创建、借用、返回以及销毁对象的机制

GenericObjectPool 的关键特性:

  1. 线程安全:GenericObjectPool 是线程安全的,可以在多线程环境中共享使用。
  2. 可配置性:提供了许多配置项,如最大对象数、最大空闲对象数、最小空闲对象数、当池中对象耗尽时的行为等。
  3. 泛型支持:它是泛型的,可以指定池中管理的对象类型。

原理和工作流程:

  1. 对象创建:
    GenericObjectPool 使用一个工厂模式来创建对象。它依赖于实现了 PooledObjectFactory 接口的工厂类来创建、初始化、激活、钝化和销毁对象。
  2. 借用对象 (borrowObject)
    当调用 borrowObject() 方法时,池尝试为请求提供一个实例。首先,它会检查池中是否有可用的空闲对象(已创建但未被借用的对象)。
  • 如果有空闲对象,池将返回一个,并将其标记为“已借用”
  • 如果没有空闲对象可用,但当前池中对象的总数未达到配置的最大数量(maxTotal),池将使用工厂创建一个新对象
  • 如果池已满(即,当前借用的对象数加上空闲对象数达到了 maxTotal),则 borrowObject() 方法的行为取决于配置的阻塞策略。它可以抛出异常阻塞等待直到有对象可用或者返回 null,具体取决于配置。
  1. 返回对象 (returnObject)
    使用完对象后,客户端应该通过调用 returnObject() 方法来归还对象到池中。
    归还操作通常涉及将对象标记为“空闲”,可能还会涉及钝化(重置或清理)对象以供将来重用。
  2. 对象销毁destroyObject()
    池可以根据配置策略(如空闲时间超过一定限制空闲对象超过一定数量等)决定销毁一些对象。
    销毁操作由工厂的 destroyObject() 方法完成。
  3. 维护活动
    GenericObjectPool 可以配置为执行后台维护线程,这个线程定期检查池中的对象根据配置执行对象的逐出(比如基于空闲时间或其他策略来逐出对象),确保池中保持活跃和清理的对象

关键配置参数:

  1. maxTotal:池中允许的最大对象数
  2. maxIdle:池中允许的最大空闲对象数
  3. minIdle:池中确保的最小空闲对象数
  4. maxWaitMillis:当池中没有可用对象时,borrowObject() 方法等待对象变为可用的最大时间(以毫秒为单位)。超时则抛出异常。
  5. testOnBorrow、testOnReturn、testWhileIdle:是否在借用、归还、空闲时检验对象

快速开始

在pom.xml 文件中,添加如下依赖:

<dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.11.1</version>
</dependency>

实现 池化对象工厂接口 PooledObjectFactory

import com.tmall.bombonera.client.model.Context;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;public class ContextPoolFactory implements PooledObjectFactory<Context> {@Overridepublic void activateObject(PooledObject<Context> pooledObject) throws Exception {// 激活对象:当这个对象下次从池中借用时,将调用 activateObject() 方法使其重新“激活”。激活通常包括将对象恢复到适合再次使用的状态pooledObject.getObject().init();}@Overridepublic void destroyObject(PooledObject<Context> pooledObject) throws Exception {}@Overridepublic PooledObject<Context> makeObject() throws Exception {// 创建新对象Context context = new Context();context.init();return new DefaultPooledObject<>(context);}@Overridepublic void passivateObject(PooledObject<Context> pooledObject) throws Exception {// 钝化对象:重置对象的状态,使其回到一个干净的初始状态,包括决定是否将对象的属性设置为 null 或重置为默认值}@Overridepublic boolean validateObject(PooledObject<Context> pooledObject) {// 验证对象是否可用:首先检查要归还的对象是否仍然有效。如果对象在使用过程中变得不再有效(例如,数据库连接关闭),那么该对象将不会被归还到池中,而是被销毁return true;}
}

定义上下文context的池子


import java.time.Duration;import javax.annotation.PostConstruct;import org.apache.commons.pool2.impl.AbandonedConfig;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;@Component
public class ContextPool {public GenericObjectPool<Context> genericObjectPool;/*** 初始化 genericObjectPool,设置工厂类、池化配置*/@PostConstructpublic void init() {ContextPoolFactory factory = new ContextPoolFactory();//设置对象池的相关参数GenericObjectPoolConfig poolConfig = initConfig();//新建一个对象池,传入对象工厂和配置genericObjectPool = new GenericObjectPool<>(factory, poolConfig);// 设置抛弃策略setAbandonedConfig(genericObjectPool);}/*** 获取一个对象** @return Context 上下文对象* @throws Exception 当获取对象失败时抛出*/public Context getContext() throws Exception {return genericObjectPool.borrowObject();}/*** 归还一个对象** @param context 上下文对象*/public void returnContext(Context context) {genericObjectPool.returnObject(context);}/*** 每5分钟打印对象池状态*/@Scheduled(fixedRate = 300000)public void printPoolStats() {UnionLogUtils.bizInfo("ContextPool stats: Created: {}, Borrowed: {}, Returned: {}, Destroyed: {}, Active: {}, Idle: {}",genericObjectPool.getCreatedCount(), genericObjectPool.getBorrowedCount(),genericObjectPool.getReturnedCount(), genericObjectPool.getDestroyedCount(),genericObjectPool.getNumActive(), genericObjectPool.getNumIdle());}/*** 池子初始化*/private GenericObjectPoolConfig initConfig() {GenericObjectPoolConfig cfg = new GenericObjectPoolConfig();cfg.setJmxNamePrefix("objectPool");//  对象总数cfg.setMaxTotal(5000);// 最大空闲对象数cfg.setMaxIdle(4000);// 最小空闲对象数cfg.setMinIdle(500);// 借对象阻塞最大等待时间// 获取资源的等待时间。blockWhenExhausted 为 true 时有效。-1 代表无时间限制,一直阻塞直到有可用的资源cfg.setMaxWait(Duration.ofMillis(10));// 最小驱逐空闲时间cfg.setMinEvictableIdleTime(Duration.ofMillis(5000));// 每次驱逐数量  资源回收线程执行一次回收操作,回收资源的数量。cfg.setNumTestsPerEvictionRun(10);// 回收资源线程的执行周期,默认 -1 表示不启用回收资源线程cfg.setTimeBetweenEvictionRuns(Duration.ofMillis(5000));// 资源耗尽时,是否阻塞等待获取资源,默认 truecfg.setBlockWhenExhausted(false);return cfg;}/*** 设置抛弃策略** @param genericObjectPool 对象池*/private void setAbandonedConfig(GenericObjectPool<Context> genericObjectPool) {// 设置抛弃策略,当对象借出较长时间未归还则进行抛弃,认为对象泄漏AbandonedConfig abandonedConfig = new AbandonedConfig();// 在Maintenance的时候检查是否有泄漏abandonedConfig.setRemoveAbandonedOnMaintenance(true);// 在borrow的时候检查是否有泄漏abandonedConfig.setRemoveAbandonedOnBorrow(true);//如果一个对象borrow之后1秒还没有返还给pool,认为是泄漏的对象abandonedConfig.setRemoveAbandonedTimeout(1);genericObjectPool.setAbandonedConfig(abandonedConfig);}
}

调用案例

@Resourceprivate ContextPool contextPool;/*** 调用底层引擎获取判定结果, 包括参数的构造** @param rtaRequest tanx请求参数* @return 巨浪rta引擎返回的结果*/private AskResultV4 getAskResult(RtaRequest rtaRequest) {Context context = null;try {// 借用对象context = contextPool.getContext();context.setChannelId(tanxRtaConfig.getChannel());context.setAdvertisingSpaceId(tanxRtaConfig.getAdvertisingSpaceId());context.setBidSwtich(tanxRtaConfig.getNeedBid());...} catch (Exception e) {} finally {if (Objects.nonNull(context)) {// 归还对象contextPool.returnContext(context);}}}

这篇关于GenericObjectPool对象池化的介绍与用法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Java如何通过反射机制获取数据类对象的属性及方法

《Java如何通过反射机制获取数据类对象的属性及方法》文章介绍了如何使用Java反射机制获取类对象的所有属性及其对应的get、set方法,以及如何通过反射机制实现类对象的实例化,感兴趣的朋友跟随小编一... 目录一、通过反射机制获取类对象的所有属性以及相应的get、set方法1.遍历类对象的所有属性2.获取

Python实现NLP的完整流程介绍

《Python实现NLP的完整流程介绍》这篇文章主要为大家详细介绍了Python实现NLP的完整流程,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 编程安装和导入必要的库2. 文本数据准备3. 文本预处理3.1 小写化3.2 分词(Tokenizatio

oracle中exists和not exists用法举例详解

《oracle中exists和notexists用法举例详解》:本文主要介绍oracle中exists和notexists用法的相关资料,EXISTS用于检测子查询是否返回任何行,而NOTE... 目录基本概念:举例语法pub_name总结 exists (sql 返回结果集为真)not exists (s

java中VO PO DTO POJO BO DO对象的应用场景及使用方式

《java中VOPODTOPOJOBODO对象的应用场景及使用方式》文章介绍了Java开发中常用的几种对象类型及其应用场景,包括VO、PO、DTO、POJO、BO和DO等,并通过示例说明了它... 目录Java中VO PO DTO POJO BO DO对象的应用VO (View Object) - 视图对象

Springboot中Jackson用法详解

《Springboot中Jackson用法详解》Springboot自带默认json解析Jackson,可以在不引入其他json解析包情况下,解析json字段,下面我们就来聊聊Springboot中J... 目录前言Jackson用法将对象解析为json字符串将json解析为对象将json文件转换为json

vue如何监听对象或者数组某个属性的变化详解

《vue如何监听对象或者数组某个属性的变化详解》这篇文章主要给大家介绍了关于vue如何监听对象或者数组某个属性的变化,在Vue.js中可以通过watch监听属性变化并动态修改其他属性的值,watch通... 目录前言用watch监听深度监听使用计算属性watch和计算属性的区别在vue 3中使用watchE

Java将时间戳转换为Date对象的方法小结

《Java将时间戳转换为Date对象的方法小结》在Java编程中,处理日期和时间是一个常见需求,特别是在处理网络通信或者数据库操作时,本文主要为大家整理了Java中将时间戳转换为Date对象的方法... 目录1. 理解时间戳2. Date 类的构造函数3. 转换示例4. 处理可能的异常5. 考虑时区问题6.

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数