设计模式学习(十二):享元模式

时间:2022-11-11 16:12:48

作者:Grey

原文地址:

博客园:设计模式学习(十二):享元模式

CSDN:设计模式学习(十二):享元模式

享元模式

享元模式是一种结构型模式。

一个应用场景是:运用共享技术有效地支持大量细粒度的对象。主要解决

在有大量对象时,有可能会造成内存溢出,我们把其*同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。

假设我们有一个子弹类,同时我们设计一个子弹池,子弹池负责提供子弹

public class BulletPool {
    List<Bullet> bullets = new ArrayList<>();
    {
        for (int i = 0; i < 10; i++) {
            bullets.add(new Bullet(true));
        }
    }
    public Bullet getBullet() {
        for (int i = 0; i < bullets.size(); i++) {
            if (bullets.get(i).living) {
                return bullets.get(i);
            }
        }
        return new Bullet(true);
    }
}

可以看到 getBullet 逻辑,如果池子中有子弹,就拿池中的子弹,如果没有,就 new 一个新的子弹返回。

上述示例的 UML 图如下

设计模式学习(十二):享元模式

享元模式应用

  • 使用对象池对高并发下的内存进行管理

对于开发者来说,垃圾回收是不可控的,而且是无法避免的。但是,我们还是可以通过一些方法来降低垃圾回收的频率,减少进程暂停的时长。我们知道,只有使用过被丢弃的对象才是垃圾回收的目标,所以,我们需要想办法在处理大量请求的同时,尽量少的产生这种一次性对象。最有效的方法就是,优化你的代码中处理请求的业务逻辑,尽量少的创建一次性对象,特别是占用内存较大的对象。比如说,我们可以把收到请求的 Request 对象在业务流程中一直传递下去,而不是每执行一个步骤,就创建一个内容和 Request 对象差不多的新对象。这里面没有多少通用的优化方法。对于需要频繁使用,占用内存较大的一次性对象,我们可以考虑自行回收并重用这些对象。实现的方法是这样的:我们可以为这些对象建立一个对象池。收到请求后,在对象池内申请一个对象,使用完后再放回到对象池中,这样就可以反复地重用这些对象,非常有效地避免频繁触发垃圾回收。

  • Java 中 Boolean 类的valueOf(boolean b) 方法 ,这个方法返回的 Boolean 对象不会新 new 出来,而是复用的同一个, 源码如下:
public static Boolean valueOf(boolean b){
    return(b?TRUE:FALSE);
}
public static final Boolean TRUE=new Boolean(true);
public static final Boolean FALSE=new Boolean(false);
  • Netty 中的 Buffer 分配。

  • 连接池管理,例如:Apache Commons Pool

  • Java SE 中的 IntegerCache 类和 String 类

在 Java Integer 的实现中, -128 到 127 之间的整型对象会被事先创建好,缓存在 IntegerCache 类中。当我们使用自动装箱或者valueOf()来创建这个数值区间的整型对象时,会复用 IntegerCache 类事先创建好的对象。这里的 IntegerCache 类就是享元工厂类,事先创建好的整型对象就是享元对象。在Java 中的 String 类的实现中,JVM 开辟一块存储区专门存储字符串常量,这块存储区叫作字符串常量池,类似于 Integer 中的 IntegerCache 。不过,跟IntegerCache 不同的是,它并非事先创建好需要共享的对象,而是在程序的运行期间,根据需要来创建和缓存字符串常量。

注:Java 提供了两个配置 IntegerCache 的参数

//方法一:
-Djava.lang.Integer.IntegerCache.high=255
//方法二:
-XX:AutoBoxCacheMax=255

UML 和 代码

UML 图

代码

更多

设计模式学习专栏

参考资料