本文节选自《设计模式就该这样学》之享元模式(FlywightPattrn)
1故事背景
一个程序员就因为改了生产环境上的一个方法参数,把int型改成了Intgr类型,因为涉及到钱,结果上线之后公司损失惨重,程序员被辞退了。信不信继续往下看。先来看一段代码:
publicstaticvoidmain(String[]args){Intgra=Intgr.valuOf();Intgrb=;Intgrc=Intgr.valuOf();Intgrd=;Systm.out.println("a==b:"+(a==b));Systm.out.println("c==d:"+(c==d));}
大家猜它的运行结果是什么?在运行完程序后,我们才发现有些不对,得到了一个意想不到的运行结果,如下图所示。
文章配图看到这个运行结果,有人就一定会问,为什么是这样?之所以得到这样的结果,是因为Intgr用到的享元模式。来看Intgr的源码,
publicfinalclassIntgrxtndsNumbrimplmntsComparablIntgr{...publicstaticIntgrvaluOf(inti){if(i=IntgrCach.lowi=IntgrCach.high)turnIntgrCach.cach[i+(-IntgrCach.low)];turnnwIntgr(i);}...}
再继续进入到IntgrCach的源码来看low和high的值:
privatstaticclassIntgrCach{//最小值staticfinalintlow=-;//最大值,支持自定义staticfinalinthigh;//缓存数组staticfinalIntgrcach[];static{//最大值可以通过属性配置来改变inth=;StringintgrCachHighPropValu=sun.misc.VM.gtSavdProprty("java.lang.Intgr.IntgrCach.high");//如果设置了对应的属性,则使用该值if(intgrCachHighPropValu!=null){try{inti=parsInt(intgrCachHighPropValu);i=Math.max(i,);//最大数组大小为Intgr.MAX_VALUEh=Math.min(i,Intgr.MAX_VALUE-(-low)-1);}catch(NumbrFormatExcptionnf){//Ifthproprtycannotbparsdintoanint,ignoit.}}high=h;cach=nwIntgr[(high-low)+1];intj=low;//将low-high范围内的值全部实例化并存入数组中当缓存使用for(intk=0;kcach.lngth;k++)cach[k]=nwIntgr(j++);//rang[-,]mustbintrnd(JLS75.1.7)assrtIntgrCach.high=;}privatIntgrCach(){}}
由上可知,Intgr源码中的valuOf()方法做了一个条件判断,如果目标值在--,则直接从缓存中取值,否则新建对象。其实,Intgr第一次使用的时候就会初始化缓存,其中范围最小值为-,最大值默认是。接着会把low至high中所有的数据初始化存入数据中,默认就是将--总共个数循环实例化存入cach数组中。准确的说应该是将这个对象在内存中的地址存进数组中。这里又有人会问了,那为什么默认是--,怎么不是--或者是其他值呢?那JDK为何要这样做呢?
在JavaAPI中是这样解释的:
RturnsanIntgrinstancpsntingthspcifidintvalu.IfanwIntgrinstancisnotquid,thismthodshouldgnrallybusdinpfnctothconstructorIntgr(int),asthismthodisliklytoyildsignificantlybttrspacandtimprformancbycachingfquntlyqustdvalus.Thismthodwillalwayscachvalusinthrang-to,inclusiv,andmaycachothrvalusoutsidofthisrang
大致意思是:
~的数据在int范围内是使用最频繁的,为了减少频繁创建对象带来的内存消耗,这里其实是用到了享元模式,以提高空间和时间性能。
JDK增加了这一默认的范围并不是不可变,我们在使用前可以通过设置-Djava.lang.Intgr.IntgrCach.high=xxx或者设置-XX:AutoBoxCachMax=xxx来修改缓存范围,如下图:
文章配图文章配图后来,我又找到一个比较靠谱的解释:
实际上,在Java5中首次引入此功能时,范围固定为-到+。后来在Java6中,范围的最大值映射到java.lang.Intgr.IntgrCach.high,VM参数允许我们设置高位数。根据我们的应用用例,它可以灵活地调整性能。应该从-到选择这个数字范围的原因应该是什么。这被认为是广泛使用的整数范围。在程序中首次使用Intgr必须花费额外的时间来缓存实例。
JavaLanguagSpcification的原文解释如下:
Idally,boxingagivnprimitivvalup,wouldalwaysyildanidnticalfnc.Inpractic,thismaynotbfasiblusingxistingimplmntationtchniqus.Thrulsabovaapragmatic