Netty’s IO Principle 2

The concept of zero copy requires context, such as DCI (Data Context Interaction) in DDD.

Using Scala’s Trait, Human can inherit SchoolTeacherRole or SchoolStudentRole, etc., so that Human has the traits of the role and automatically blocks traits other than this role.

If the boundary is transferred to the JVM memory area, it is the context of the JVM. If the copy of the array to DirectByteBuffer is removed, it is a true zero copy.

struct (can store a continuous space of the same or different types), array, linked list

The position of the pointer when the Buffer array is initialized:

Write 2Byte data:

If you want to read data at this time, just set the Position to 0, and you also need a pointer to indicate the number of readable bytes Limit:

At this time, if you want to read the Limit position after reading it, you can set the Position to the Mark position and you can read it again.

So whether it is DirectByteBuffer or HeapBytbuffer, they will have these four pointers, so they are abstracted and placed in the Buffer abstract class. The operation methods of these four pointers are defined in the abstract class.

// Invariants: mark <= position <= limit <= capacity
private int mark = -1;
private int position = 0;
private int limit;
private int capacity;

// Used only by direct buffers
// NOTE: hoisted here for speed in JNI GetDirectBufferAddress
long address;
public interface DirectBuffer {
    long address();

    Object attachment();
    //Clean up off-heap memory
    Cleaner cleaner();
}
class DirectByteBuffer extends MappedByteBuffer implements DirectBuffer
{
    DirectByteBuffer(int cap) { // package-private
        super(-1, 0, cap, cap);
        boolean pa = VM.isDirectMemoryPageAligned();
        int ps = Bits.pageSize();
        long size = Math.max(1L, (long)cap + (pa ? ps : 0));
        Bits.reserveMemory(size, cap);

        long base = 0;
        try {
            base = unsafe.allocateMemory(size);//Application for memory space
        } catch (OutOfMemoryError x) {
            Bits.unreserveMemory(size, cap);
            throw x;
        }
        unsafe.setMemory(base, size, (byte) 0);
        if (pa & amp; & amp; (base % ps != 0)) {
            // Round up to page boundary
            address = base + ps - (base & amp; (ps - 1));
        } else {
            address = base;
        }
        cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
        att = null;
    }
    //The run method in Deallocator will be called when clearing
    private static class Deallocator implements Runnable
        {
            public void run() {
                if (address == 0) {
                    // Paranoia
                    return;
                }
                unsafe.freeMemory(address);
                address = 0;
                Bits.unreserveMemory(size, capacity);
            }
        }
}
public class Cleaner extends PhantomReference<Object> {
    public static Cleaner create(Object var0, Runnable var1) {
        return var1 == null ? null : add(new Cleaner(var0, var1));
    }
    public void clean() {
        if (remove(this)) {
            try {
                this.thunk.run();
            } catch (final Throwable var2) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        if (System.err != null) {
                            (new Error("Cleaner terminated abnormally", var2)).printStackTrace();
                        }
                        System.exit(1);
                        return null;
                    }
                });
            }
        }
    }
}

So who called the run method of Deallocator?

public abstract class Reference<T> {
    volatile ReferenceQueue<? super T> queue;
    private static class ReferenceHandler extends Thread {
        ReferenceHandler(ThreadGroup g, String name) {
            super(g, name);
        }
        public void run() {
            while (true) {
                tryHandlePending(true);
            }
        }
    }
    static boolean tryHandlePending(boolean waitForNotify) {
        Reference<Object> r;
        Cleaner c;
        try {
            synchronized (lock) {
                if (pending != null) {
                    r = pending;
                    c = r instanceof Cleaner ? (Cleaner) r : null;
                    // unlink 'r' from 'pending' chain
                    pending = r.discovered;
                    r.discovered = null;
                } else {
                    if (waitForNotify) {
                        lock.wait();
                    }
                    return waitForNotify;
                }
            }
        } catch (OutOfMemoryError x) {
            Thread.yield();
            return true;
        } catch (InterruptedException x) {
            // retry
            return true;
        }
        // Fast path for cleaners
        if (c != null) {
            c.clean();
            return true;
        }
        ReferenceQueue<? super Object> q = r.queue;
        if (q != ReferenceQueue.NULL) q.enqueue(r);
        return true;
    }
}

MappedByteBuffer (OS memory mapped directly to user space memory):

The knowledge points of the article match the official knowledge files, and you can further learn related knowledge. Java Skill TreeIO Flow Overview 138,710 people are learning the system