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> {
    @Override
    public void activateObject(PooledObject<Context> pooledObject) throws Exception {
        // 激活对象:当这个对象下次从池中借用时,将调用 activateObject() 方法使其重新“激活”。激活通常包括将对象恢复到适合再次使用的状态
        pooledObject.getObject().init();
    }

    @Override
    public void destroyObject(PooledObject<Context> pooledObject) throws Exception {
    }

    @Override
    public PooledObject<Context> makeObject() throws Exception {
        // 创建新对象
        Context context = new Context();
        context.init();
        return new DefaultPooledObject<>(context);
    }

    @Override
    public void passivateObject(PooledObject<Context> pooledObject) throws Exception {
    	// 钝化对象:重置对象的状态,使其回到一个干净的初始状态,包括决定是否将对象的属性设置为 null 或重置为默认值
    }

    @Override
    public 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,设置工厂类、池化配置
     */
    @PostConstruct
    public 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));
        // 资源耗尽时,是否阻塞等待获取资源,默认 true
        cfg.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);
    }
}

调用案例


    @Resource
    private 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.mfbz.cn/a/713933.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

多设备互通、开箱即用的私有化笔记软件,极空间部署最强备忘录项目『Memos』

多设备互通、开箱即用的私有化笔记软件&#xff0c;极空间部署最强备忘录项目『Memos』 哈喽小伙伴们好&#xff0c;我是Stark-C~ 手机上的备忘录我想绝大多数的小伙伴都会用到&#xff0c;日常用来记录一下生活中的消费开支清单&#xff0c;或者工作中记录一些重要的任务或项…

【动态规划】0-1背包问题

【动态规划】0-1背包问题 题目:现在有四个物品&#xff0c;背包总容量为8&#xff0c;背包最多能装入价值为多少的物品? 我的图解 表格a【i】【j】表示的是容量为j的背包装入前i个物品的最大价值。 拿a【1】【1】来说&#xff0c;它的值就是背包容量为1&#xff0c;只考虑…

4.1 初探Spring Boot

初探Spring Boot实战概述 Spring Boot简介 Spring Boot是一个开源的Java框架&#xff0c;由Pivotal团队&#xff08;现为VMware的一部分&#xff09;开发&#xff0c;旨在简化Spring应用程序的创建和部署过程。它通过提供一系列自动化配置、独立运行的特性和微服务支持&#…

低代码开发MES系统,一周实现数字化

随着工业4.0和智能制造的兴起&#xff0c;企业对于生产过程的数字化、智能化需求日益迫切。制造执行系统&#xff08;MES&#xff09;作为连接计划层与控制层的关键信息系统&#xff0c;在提升生产效率、优化资源配置、保障产品质量等方面发挥着重要作用。然而&#xff0c;传统…

数据质量管理解决方案(55页PPT)

方案介绍&#xff1a; 数据质量管理解决方案是一个系统性的方法&#xff0c;旨在确保数据的准确性、完整性、一致性、可靠性和可用性。该解决方案覆盖了数据从产生到消亡的整个生命周期&#xff0c;包括数据的计划、获取、存储、共享、维护、应用和消亡等各个阶段。数据质量管…

IDEA导入项目报错java程序包不存在

如图文件结构&#xff0c;本来是在web-demo中操作&#xff0c;但是想导入一下其他模块&#xff0c;切换了项目文件的目录&#xff0c;发现需要重新对Tomcat等进行配置&#xff0c;配置好之后发现运行出现Java相关错误&#xff08;如下&#xff09;记录一下修正过程。 java: 程序…

【教程】Linux设置进程的优先级

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 关键指令 sudo chrt -f <优先级> <指令> 示例脚本 当然也可以不是启动Python脚本&#xff0c;普通的指令都可以&#xff0c;可自行适当修…

2024/6/16 英语每日一段

Nature has the means--to a degree--to limit the effects of climate change. Intact ecosystems such as forests, grasslands, oceans and peatlands are “carbon sinks”--natural storage systems that remove atmospheric carbon and other greenhouse gases--and are …

Intel HDSLB 高性能四层负载均衡器 — 代码剖析和高级特性

目录 文章目录 目录前言代码剖析软件架构目录结构配置解析启动流程分析数据面 jobs 注册数据面 jobs 执行 转发流程分析收包阶段L2 处理阶段L3 处理阶段L4 处理阶段 高级特性大象流转发优化快慢路径分离转发优化报文基础转发优化 最后参考文档 前言 在前 2 篇文章中&#xff0…

【云原生】Kubernetes----Kubernetes集群部署Prometheus 和Grafana

目录 引言 一、环境准备 二、部署node-exporter &#xff08;一&#xff09;创建命名空间 &#xff08;二&#xff09;部署node-exporter 1.获取镜像 2.定义yaml文件 3.创建服务 4.查看监控数据 三、部署Prometheus &#xff08;一&#xff09;创建账号并授权 &…

Java学习笔记之基本数据类型转换

前言 本篇文章是基于我本人在初学JAVA阶段想记录的的学习笔记&#xff0c;如有错误&#xff0c;恳请指正。今天要干掉的是JAVA的基本数据类型转换 Java的基本数据类型转换 前言一&#xff0c;基本数据类型复习二&#xff0c;基本介绍什么是自动类型转换&#xff1f; 三&#…

【Numpy】一文向您详细介绍 np.round()

【Numpy】一文向您详细介绍 np.round() 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a;985高校的普通本硕&#xff0c;…

从0到1搭建MCU芯片上操作系统环境。开发都需要哪些环节和准备

MCU芯片环境搭建与操作系统上载步骤 1. 硬件准备 选择合适的MCU芯片&#xff0c;例如STM32、GD32等。 准备开发板&#xff0c;用于硬件连接和实验。 准备必要的外围设备&#xff0c;如电源适配器、USB转串口模块等。 2. 软件环境搭建 安装编程语言环境&#xff0c;如C/C编译…

NVIDIA Triton系列02-功能与架构简介

NVIDIA Triton系列02-功能与架构简介 B站&#xff1a;肆十二-的个人空间-肆十二-个人主页-哔哩哔哩视频 (bilibili.com) 博客&#xff1a;肆十二-CSDN博客 问答&#xff1a;(10 封私信 / 72 条消息) 肆十二 - 知乎 (zhihu.com) 前面文章介绍微软 Teams 会议系统、微信软件与腾讯…

微信视频号视频怎么下载才能保存视频到手机相册,推荐一款稳定的视频号下载工具

视频号视频下载发现写了很多次&#xff0c;竟然还有很多人不知道微信视频号视频怎么下载&#xff0c;今天就来说说这款视频号下载工具。 视频号下载工具介绍 这款视频号下载工具叫视频号下载plus&#xff0c;也有很多人称之为视频下载小助手不知道的可以自行百度。 注意在百度…

码住!详解时序数据库不同分类与性能对比

加速发展中的时序数据库&#xff0c;基于不同架构&#xff0c;最流行的类别是&#xff1f; 作为管理工业场景时序数据的新兴数据库品类&#xff0c;时序数据库凭借着对海量时序数据的高效存储、高可扩展性、时序分析计算等特性&#xff0c;一跃成为物联网时代工业领域颇受欢迎的…

SolarLab - hackthebox

简介 靶机名称&#xff1a;SolarLab 难度&#xff1a;中等 靶场地址&#xff1a;https://app.hackthebox.com/machines/SolarLab 本地环境 靶机IP &#xff1a;10.10.11.16 ubuntu渗透机IP(ubuntu 22.04)&#xff1a;10.10.16.17 windows渗透机IP&#xff08;windows11&…

RawChat:优化AI对话体验,全面兼容GPT功能平台

文章目录 一、Rawchat简介1.1 RawChat的主要特性1.2 RawChat的技术原理简述 二、使用教程三、案例应用3.1 图片内容分析3.2 生图演示3.3 文档解析3.4 探索更多 四、小结 一、Rawchat简介 RawChat平台的诞生&#xff0c;其核心理念是降低用户访问类似ChatGPT这类先进AI服务的门…

FPGA - 数 - 加减乘除

一&#xff0c;数的表示 首先&#xff0c;将二进制做如下解释&#xff1a; 2的0次方1 2的1次方2 2的2次方4 2的3次方8 ..... 以此类推&#xff0c;那么任何整数&#xff0c;或者说任意一个自然数均可以采用这种方式来表示。 例如&#xff0c;序列10101001&#xff0c;根据上述…

ThinkPHP邮件发送配置教程?怎么配置群发?

ThinkPHP邮件发送安全性如何保障&#xff1f;ThinkPHP如何实现&#xff1f; 无论是用户注册后的验证邮件&#xff0c;还是订单处理的通知邮件&#xff0c;都需要一个可靠的邮件发送机制。AokSend将详细介绍如何在ThinkPHP框架中配置邮件发送功能&#xff0c;并带您逐步了解其中…