在进入正题以前,这里先提出一个问题,如何在多线程中去对一个数字进行+1操做?这个问题很是简单,哪怕是Java的初学者都能回答上来,使用AtomicXXX,好比有一个int类型的自加,那么你可使用AtomicInteger 代替int类型进行自加。java
AtomicInteger atomicInteger = new AtomicInteger(); atomicInteger.addAndGet(1);
如上面的代码所示,使用addAndGet便可保证多线程中相加,具体原理在底层使用的是CAS,这里就不展开细讲。基本上AtomicXXX能知足咱们的全部需求,直到前几天一个群友(ID:皮摩)问了我一个问题,他发如今不少开源框架中,例如Netty中的AbstractReferenceCountedByteBuf 类中定义了一个refCntUpdater:数据库
private static final AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> refCntUpdater; static { AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> updater = PlatformDependent.newAtomicIntegerFieldUpdater(AbstractReferenceCountedByteBuf.class, "refCnt"); if (updater == null) { updater = AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt"); } refCntUpdater = updater; }
refCntUpdater 是Netty用来记录ByteBuf被引用的次数,会出现并发的操做,好比增长一个引用关系,减小一个引用关系,其retain方法,实现了refCntUpdater的自增:数组
private ByteBuf retain0(int increment) { for (;;) { int refCnt = this.refCnt; final int nextCnt = refCnt + increment; // Ensure we not resurrect (which means the refCnt was 0) and also that we encountered an overflow. if (nextCnt <= increment) { throw new IllegalReferenceCountException(refCnt, increment); } if (refCntUpdater.compareAndSet(this, refCnt, nextCnt)) { break; } } return this; }
俗话说有因必有果,netty多费力气作这些事必然是有本身的缘由的,接下来就进入咱们的正题。性能优化
在java.util.concurrent.atomic
包中有不少原子类,好比AtomicInteger,AtomicLong,LongAdder等已是你们熟知的经常使用类,在这个包中还有三个类在jdk1.5中都存在了,可是常常被你们忽略,这就是fieldUpdater:多线程
这个在代码中不常常会有,可是有时候能够做为性能优化的工具出场,通常在下面两种状况会使用它:并发
this.variable
,可是你也想时不时的使用一下CAS操做或者原子自增操做,那么你可使用fieldUpdater。通常有两种状况须要正常引用:框架
.get()
和.set()
方法,这样作会增长很多的工做量,而且还须要大量的回归测试。BufferedInputStream
中,有一个buf数组用来表示内部缓冲区,它也是一个volatile数组,在BufferedInputStream中大多数时候只须要正常的使用这个数组缓冲区便可,在一些特殊的状况下,好比close的时候须要使用compareAndSet
,咱们可使用AtomicReference,我以为这样作有点乱,使用fieldUpdater来讲更加容易理解,protected volatile byte buf[]; private static final AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater = AtomicReferenceFieldUpdater.newUpdater (BufferedInputStream.class, byte[].class, "buf"); public void close() throws IOException { byte[] buffer; while ( (buffer = buf) != null) { if (bufUpdater.compareAndSet(this, buffer, null)) { InputStream input = in; in = null; if (input != null) input.close(); return; } // Else retry in case a new buf was CASed in fill() } }
以前说过在不少开源框架中都能看见fieldUpdater的身影,其实大部分的状况都是为了节约内存,为何其会节约内存呢?工具
咱们首先来看看AtomicInteger类:性能
public class AtomicInteger extends Number implements java.io.Serializable { private static final long serialVersionUID = 6214790243416807050L; // setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } private volatile int value; }
在AtomicInteger成员变量只有一个int value
,彷佛好像并无多出内存,可是咱们的AtomicInteger是一个对象,一个对象的正确计算应该是 对象头 + 数据大小,在64位机器上AtomicInteger对象占用内存以下:测试
关闭指针压缩: 16(对象头)+4(实例数据)=20不是8的倍数,所以须要对齐填充 16+4+4(padding)=24
开启指针压缩(-XX:+UseCompressedOop): 12+4=16已是8的倍数了,不须要再padding。
因为咱们的AtomicInteger是一个对象,还须要被引用,那么真实的占用为:
而fieldUpdater是staic final
类型并不会占用咱们对象的内存,因此使用fieldUpdater的话能够近似认为只用了4字节,这个再未关闭指针压缩的状况下节约了7倍,关闭的状况下节约了4倍,这个在少许对象的状况下可能不明显,当咱们对象有几十万,几百万,或者几千万的时候,节约的可能就是几十M,几百M,甚至几个G。
好比在netty中的AbstractReferenceCountedByteBuf,熟悉netty的同窗都知道netty是本身管理内存的,全部的ByteBuf都会继承AbstractReferenceCountedByteBuf,在netty中ByteBuf会被大量的建立,netty使用fieldUpdater用于节约内存。
在阿里开源的数据库链接池druid中也有一样的体现,早在2012的一个pr中,就有优化内存的comment:,在druid中,有不少统计数据对象,这些对象一般会以秒级建立,分钟级建立新的,druid经过fieldUpdater节约了大量内存:
AtomicFieldUpdater的确在咱们平时使用比较少,可是其也值得咱们去了解,有时候在特殊的场景下的确能够做为奇技淫巧。
若是你们以为这篇文章对你有帮助,你的关注和转发是对我最大的支持,O(∩_∩)O: