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);}}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/28258.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【日常刷题】为什么二分法不建议使用 (right + left) / 2?

为什么二分法不建议使用 (right left) / 2&#xff1f; 用left(right-left)/2,而不用(leftright)/2是担心后者(rightleft)的值过大超过了整形的取值范围造成溢出&#xff0c;使结果不准确 就拿奇偶个数来看就知道什么原因了。 valueOf() 当有一个参数时AAA.valueOf(BBB)的…

跨域资源共享(CORS)问题与解决方案

跨域资源共享&#xff08;CORS&#xff0c;Cross-Origin Resource Sharing&#xff09;是现代web开发中常见且重要的一个概念。它涉及到浏览器的同源策略&#xff08;Same-Origin Policy&#xff09;&#xff0c;该策略用于防止恶意网站从不同来源窃取数据。然而&#xff0c;在…

Web前端与软件测试:探索技术与质量的双重世界

Web前端与软件测试&#xff1a;探索技术与质量的双重世界 在数字化时代的浪潮中&#xff0c;Web前端技术和软件测试扮演着举足轻重的角色。它们犹如一对默契的舞者&#xff0c;在技术的舞台上共同演绎着精彩绝伦的舞蹈。本文将从四个方面、五个方面、六个方面和七个方面&#…

Java实现Mysql批量插入与更新

第一、批量插入语句 Insert({"<script>","INSERT INTO TABLE_NAME (" "ID," "IS_DELETE," "GMT_CREATE," "GMT_MODIFIED" ")VALUES","<foreach collection list item item separator …

docker容器基本原理简介

一、docker容器实例运行的在linux上是一个进程 1&#xff09;、我们通过docker run 通过镜像运行启动的在linux上其实是一个进程&#xff0c;例如我们通过命令运行一个redis&#xff1a; docker run -d --name myredis redis2&#xff09;、可以看到首先我们本地还没有redis镜…

【LeetCode最详尽解答】11-盛最多水的容器 Container-With-Most-Water

欢迎收藏Star我的Machine Learning Blog:https://github.com/purepisces/Wenqing-Machine_Learning_Blog。如果收藏star, 有问题可以随时与我交流, 谢谢大家&#xff01; 链接&#xff1a; 11-盛最多水的容器 直觉 这个问题可以通过可视化图表来理解和解决。 通过图形化这个…

「动态规划」如何求乘积最大子数组?

152. 乘积最大子数组https://leetcode.cn/problems/maximum-product-subarray/description/ 给你一个整数数组nums&#xff0c;请你找出数组中乘积最大的非空连续子数组&#xff08;该子数组中至少包含一个数字&#xff09;&#xff0c;并返回该子数组所对应的乘积。测试用例的…

【数据结构】初识集合深入剖析顺序表(Arraylist)

【数据结构】初识集合&深入剖析顺序表&#xff08;Arraylist&#xff09; 集合体系结构集合的遍历迭代器增强for遍历lambda表达式 List接口中的增删查改List的5种遍历ArrayList详解ArrayList的创建ArrayList的增删查改ArrayList的遍历ArrayList的底层原理 &#x1f680;所属…

【全栈实战】大模型自学:从入门到实战打怪升级,20W字总结(一)

&#x1f60a;你好&#xff0c;我是小航&#xff0c;一个正在变秃、变强的文艺倾年。 &#x1f514;本栏讲解【全栈实战】大模型自学&#xff1a;从入门到实战打怪升级。 &#x1f514;专栏持续更新&#xff0c;适合人群&#xff1a;本科生、研究生、大模型爱好者&#xff0c;期…

JVM-GC-什么是垃圾

JVM-GC-什么是垃圾 前言 所谓垃圾其实是指&#xff0c;内存中没用的数据&#xff1b;没有任何引用指向这块内存&#xff0c;或者没有任何指针指向这块内存。没有的数据应该被清除&#xff0c;垃圾的处理其实是内存管理问题。 JVM虽然不直接遵循冯诺依曼计算机体系架构&#…

基于flask的网站如何使用https加密通信-问题记录

文章目录 项目场景&#xff1a;问题1问题描述原因分析解决步骤解决方案 问题2问题描述原因分析解决方案 参考文章 项目场景&#xff1a; 项目场景&#xff1a;基于flask的网站使用https加密通信一文中遇到的问题记录 问题1 问题描述 使用下面的命令生成自签名的SSL/TLS证书和…

Docker镜像技术剖析

目录 1、概述1.1 什么是镜像&#xff1f;1.2 联合文件系统UnionFS1.3 bootfs和rootfs1.4 镜像结构1.5 镜像的主要技术特点1.5.1 镜像分层技术1.5.2 写时复制(copy-on-write)策略1.5.3 内容寻址存储(content-addressable storage)机制1.5.4 联合挂载(union mount)技术 2.机制原理…

用PHP来调用API给自己定制一个“每日新闻”

头条新闻汇聚了互联网上的时事动态&#xff0c;提供最新新闻动态、网络热门话题和视频更新等&#xff0c;覆盖社会、政治、体育、经济、娱乐、科技等多个领域&#xff0c;并不断刷新内容。企业应用这一接口后&#xff0c;可以快速吸引更多的用户访问自己的平台。即使是非新闻类…

面向对象的三大特性与类图

1. 面向对象编程的三大特点 Object-oriented programming (OOP) is a paradigm centered around the concept of objects, which can contain data and code to manipulate that data. The three major characteristics of object-oriented programming are encapsulation, in…

天童美语:为了得体退出的那一天,你一定要好好爱孩子

父母最大的成就就是孩子可以独立&#xff0c;自己完成自己的人生。为了得体退出的那一天&#xff0c;你一定要好好爱你的小孩&#xff0c;因为每一天都在过去。当我们站在孩子成长的十字路口&#xff0c;面对那个终将到来的退出时刻&#xff0c;心中总会涌起一股难以言表的情感…

有趣的傅里叶变换与小波变换对比(Python)

不严谨的说&#xff0c;时域和频域分析就是在不同的空间看待问题的&#xff0c;不同空间所对应的原子(基函数)是不同的。你想一下时域空间的基函数是什么&#xff1f;频域空间的基函数是什么&#xff1f;一般的时-频联合域空间的基函数是什么&#xff1f;小波域空间的基函数是什…

摄影师在人工智能竞赛中与机器较量并获胜

摄影师在人工智能竞赛中与机器较量并获胜 自从生成式人工智能出现以来&#xff0c;由来已久的人机大战显然呈现出一边倒的态势。但是有一位摄影师&#xff0c;一心想证明用人眼拍摄的照片是有道理的&#xff0c;他向算法驱动的竞争对手发起了挑战&#xff0c;并取得了胜利。 迈…

课时157:脚本发布_简单脚本_功能函数

2.1.3 功能函数 学习目标 这一节&#xff0c;我们从 基础知识、简单实践、小结 三个方面来学习 基础知识 简介 需求&#xff1a;三条命令其实是一个组合&#xff0c;实现的是一个功能简单实践 实践 查看脚本内容 #!/bin/bash # 功能&#xff1a;打包代码 # 版本: v0.3 #…

代码随想录第29天|贪心算法part3

134.加油站 首先如果总油量减去总消耗大于等于零那么一定可以跑完一圈 每个加油站的剩余量rest[i]为gas[i] - cost[i] 从0开始累加rest[i]&#xff0c;和记为curSum&#xff0c;一旦curSum小于零&#xff0c;说明[0, i]区间都不能作为起始位置 因为我们一直维护的是一个剩余量大…

HCIA11 网络安全之本地 AAA 配置实验

AAA 提供 Authentication&#xff08;认证&#xff09;、Authorization&#xff08;授权&#xff09;和 Accounting&#xff08;计费&#xff09;三种安全功能。 • 认证&#xff1a;验证用户是否可以获得网络访问权。 • 授权&#xff1a;授权用户可以使用哪些服务。 •…