设计模式学习(十二):享元模式
作者:Grey
原文地址:
享元模式#
享元模式是一种结构型模式。
一个应用场景是:运用共享技术有效地支持大量细粒度的对象。主要解决
在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。
假设我们有一个子弹类,同时我们设计一个子弹池,子弹池负责提供子弹
public class BulletPool {
List 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