百日筑基第三十一天-对象池复用实践

百日筑基第三十一天-对象池复用实践

什么是对象池

对象池顾名思义就是存放对象的池,与我们常听到的线程池、数据库连接池、http连接池等一样,都是典型的池化设计思想。当需要创建对象时,先在池子中获取,如果池子中没有符合条件的对象,再进行创建新对象,同样,当对象需要销毁时,不做真正的销毁,而是将其setActive(false),并存入池子中。这样就避免了大量对象的创建。

对象池的优点: 可以集中管理池中对象,减少频繁创建和销毁长期使用的对象,从而提升复用性,以节约资源的消耗,可以有效避免频繁为对象分配内存和释放堆中内存,进而减轻jvm垃圾收集器的负担,避免内存抖动。

对象池的缺点: 会生成脏对象,因为当对象被放回对象池后,还保留着刚刚被客户端调用时生成的数据。脏对象持有上次的使用,导致内存泄漏等问题。如果下一次使用时没有清理,可能影响程序的处理数据。脏对象的生命周期比普通对象长久。维持大量的对象池也比较占用内存空间。

设计对象池需要注意什么

【1】确保线程安全。
【2】合理设置池子大小。如果对象池没有限制,可能导致对象池持有过多的闲置对象,增加内存的占用。如果对象池闲置过小,没有可用的对象时,会造成之前对象池无可用的对象时,再次请求出现的问题。现在Java的对象分配操作不比C语言的malloc调用慢, 对于轻中量级的对象, 分配/释放对象的开销可以忽略不计,并发环境中, 多个线程可能(同时)需要获取池中对象, 进而需要在堆数据结构上进行同步或者因为锁竞争而产生阻塞, 这种开销要比创建销毁对象的开销高数百倍;由于池中对象的数量有限, 势必成为一个可伸缩性瓶颈;很难正确的设定对象池的大小, 如果太小则起不到作用, 如果过大, 则占用内存资源高。对象池属于空间换时间的折中。
【3】制定合理的驱逐策略。
【4】确保对象归还后,外部没有地方持有该对象的引用。

common-pool2组件介绍

【1】Apache Common Pool2Apache提供的一个通用对象池技术实现,可以方便定制化自己需要的对象池。
【2】Maven依赖

<dependency>  
    <groupId>org.apache.commons</groupId>   
    <artifactId>commons-pool2</artifactId>  
    <version>${version}</version>  
</dependency>

【3】使用案例

public class ByteArrayOutputStreamFactory extends BasePooledObjectFactory<ByteArrayOutputStream> {  
    private static final int DEFAULT_SIZE = 1024;  

    @Override  
    public ByteArrayOutputStream create() throws Exception {  
        return new ByteArrayOutputStream(DEFAULT_SIZE);  
    }  

    @Override  
    public PooledObject<ByteArrayOutputStream> wrap(ByteArrayOutputStream s) {  
        return new DefaultPooledObject<>(s);  
    }  

    @Override  
    public void activateObject(PooledObject<ByteArrayOutputStream> p) {  
        p.getObject().reset();  
    }  
}  

public class ByteArrayStreamPool {
    private static final GenericObjectPool<ByteArrayOutputStream> POOL;

    static {
        // 根据需求自定义配置
        GenericObjectPoolConfig config = new GenericObjectPoolConfig();
        cfg.setJmxNamePrefix("objectPool");
        // 资源耗尽时,是否阻塞等待获取资源,默认 true
        config.setBlockWhenExhausted(false);
        // 回收资源线程的执行周期,默认 -1 表示不启用回收资源线程
        config.setTimeBetweenEvictionRunsMillis(10000);
        //  对象总数
        cfg.setMaxTotal(sxInferConfig.getPoolMaxTotal());
        // 最大空闲对象数
        cfg.setMaxIdle(sxInferConfig.getPoolMaxIdle());
        // 最小空闲对象数
        cfg.setMinIdle(sxInferConfig.getPoolMinIdle());
        // 借对象阻塞最大等待时间
        // 获取资源的等待时间。blockWhenExhausted 为 true 时有效。-1 代表无时间限制,一直阻塞直到有可用的资源
        cfg.setMaxWaitMillis(sxInferConfig.getPoolMaxWait());
        // 最小驱逐空闲时间
        cfg.setMinEvictableIdleTimeMillis(sxInferConfig.getPoolMinEvictableIdleTimeMillis());
        // 每次驱逐数量  资源回收线程执行一次回收操作,回收资源的数量。默认 3
        cfg.setNumTestsPerEvictionRun(sxInferConfig.getPoolNumTestsPerEvictionRun());
        POOL = new GenericObjectPool<>(new ByteArrayOutputStreamFactory(), config);
    }

    private ByteArrayStreamPool() {
    }

    public static GenericObjectPool<ByteArrayOutputStream> get() {
        return POOL;
    }
}

// 注意:pool须定义为单例(不要频繁创建对象池,这比频繁创建对象更糟糕)  
GenericObjectPool<ByteArrayOutputStream> pool = ByteArrayStreamPool.get();  
ByteArrayOutputStream stream = null;  
try {  
    stream = pool.borrowObject();  
} catch (NoSuchElementException ex) {  
    // 自行创建一个对象  
    stream = new ByteArrayOutputStream(1024);  
}  

// do something  

try {  
    // 自行创建的对象returnObject会抛出异常  
    pool.returnObject(stream);   
}  catch(Exception ex) {  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值