1. The background

Before we get to the point, here’s a question: how do I +1 a number in multiple threads? This question is simple enough that even Java beginners can answer it. Using AtomicXXX, for example, if you have an int autoappend, you can use AtomicInteger instead of an int to autoappend.

 AtomicInteger atomicInteger = new AtomicInteger();
        atomicInteger.addAndGet(1);
Copy the code

As shown in the code above, addAndGet can be used to ensure the addition of multiple threads. The specific principle is CAS at the bottom, so I won’t go into details here. AtomicXXX basically met all of our needs, until a group member asked me a question the other day, and he found that in many open source frameworks, For example in the Netty AbstractReferenceCountedByteBuf class defines a 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;
    }
Copy the code

RefCntUpdater Netty is used to record ByteBuf cited times, there will be a concurrent operations, such as increasing the relationship between a reference and a reference relationship, its retain method, implements the refCntUpdater since increased:

    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;
    }
Copy the code

As the saying goes, every cause has an effect. Netty must have its own reasons for making such efforts.

2.Atomic field updater

In Java. Util. Concurrent. Atomic package has a lot of atomic classes, such as AtomicInteger AtomicLong, LongAdder etc is known as the common class, there are three classes in this package in jdk1.5 exists, but often overlooked by people, This is fieldUpdater:

  • AtomicIntegerFieldUpdater
  • AtomicLongFieldUpdater
  • AtomicReferenceFieldUpdater

This doesn’t happen very often in code, but it can sometimes be used as a performance tuning tool, and it’s used in two general situations:

  • You want to use volatile through normal references, such as directly from a classthis.variableBut if you want to use CAS or atomic increment from time to time, you can use fieldUpdater.
  • When using AtomicXXX, you can save memory by using fieldUpdater when there are multiple objects that refer to Atomic objects.

2.1 Refer to volatile variables normally

There are two situations that require normal reference:

  1. We can replace the AtomicXXX object when we introduce a new CAS requirement in code that already has a normal reference, but the previous calls have to be replaced.get()and.set()Method, which adds a lot of work and requires a lot of regression testing.
  2. The code is easier to understand inBufferedInputStreamIn BufferedInputStream, there is a buF array that represents the internal buffer, which is also a volatile array. In BufferedInputStream, the array buffer is used normally most of the time, but in special cases, such as closecompareAndSet, we can use AtomicReference, which I find a little confusing, and it’s easier to understand with 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()
        }
    }
Copy the code

2.2 Memory Saving

FieldUpdater can be found in many open source frameworks, most of which are designed to save memory. Why?

Let’s start with the AtomicInteger class:

public class AtomicInteger extends Number implements java.io.Serializable { private static final long serialVersionUID =  6214790243416807050L; // setup to use Unsafe.compareAndSwapIntfor 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;
}
Copy the code

The AtomicInteger member variable has only one int value, which seems to have no extra memory, but our AtomicInteger is an object, and the correct calculation of an object should be the object header + the data size, On 64-bit machines, the AtomicInteger object takes up the following memory:

  • Turn pointer compression off: 16(object header)+4(instance data)=20 is not a multiple of 8, so align padding 16+4+4(padding)=24

  • Enable pointer compression (-xx :+UseCompressedOop): 12+4=16 is already a multiple of 8, no padding required.

Since our AtomicInteger is an object and needs to be referenced, the real usage is:

  • Turn off pointer compression: 24 + 8 = 32
  • Enable pointer compression: 16 + 4 = 20

FieldUpdater is a Staic final type and does not occupy the memory of our object, so using fieldUpdater can be approximately considered as using only 4 bytes, which saves 7 times when pointer compression is not turned off, and 4 times when pointer compression is turned off. This may not be obvious ina small number of objects. When we have hundreds of thousands, or millions, or tens of millions of objects, the savings may be tens of meters, hundreds of meters, or even gigabytes.

Such as in the netty AbstractReferenceCountedByteBuf, familiar with netty classmates know netty is own memory management, all ByteBuf inherit AbstractReferenceCountedByteBuf, ByteBuf is created in large numbers in Netty, which uses fieldUpdater to save memory.

In ali open source database connection pool Druid is also reflected, as early as 2012 in a PR, there is a comment on memory optimization:

3. The last

AtomicFieldUpdater is rarely used in our daily life, but it is also worth our understanding. Sometimes it can be used as a strange technique in special situations.

If you find this article helpful to you, your attention and forwarding will be my biggest support.

This article was automatically published by ArtiPub