Netty 직접 메모리 원리 및 응용 분석

25431 단어 Netty메모리 관리
1.일반적인 메모리 모델 개요
일반적으로 시스템 은 시스템 자체 의 안전성 과 건장 성 을 확보 하기 위해 메모리 를 논리 적 으로 커 널 구역 과 사용자 구역 으로 격 리 시 켜 이해 하기 쉽다.사용자 의 행위 가 통제 불 능 성 이 너무 강하 고 너무 많이 노출 되면 각종 신기 한 용법 을 초래 하여 시스템 의 통제 범 위 를 초과 하기 쉽다.물론 어떤 언어 는 메모 리 를 직접 제어 하 는 것 을 지원 합 니 다.예 를 들 어 C.포인터 로 메모리 의 거의 임 의 위치 에 있 는 데 이 터 를 방문 할 수 있 습 니 다(하드웨어 주 소 를 제외 하고).어 셈 블 리 와 같이 임의의 주 소 를 방문 할 수 있다.이런 밑바닥 의 언어 는 우리 에 게 서 점점 멀 어 지고 있다.이것 은 기본적으로 일반 프로그래머 와 관계 가 크 지 않다.
사용자 가 많은 시간 동안 프로 그래 밍 통 제 는 모두 사용자 구역 에서 이 루어 집 니 다.예 를 들 어 저 는 덧셈 과 곱셈 을 합 니 다.예 를 들 어 Integer a=2;Integer b = 3; Integer c = a * b; 이런 조작 은 모든 조작 이 사용자 공간 에서 이 루어 진 것 이다.이 조작 들 은 내부 핵 구역 의 개입 이 없 을 것 이다.그러나 일부 조작 은 반드시 커 널 에서 진행 해 야 한다.예 를 들 어 파일 에 대한 읽 기와 쓰기,즉 서로 다른 장치 간 의 데이터 교환,즉 io 류 작업 이다.이런 조작 은 매우 어 려 운 실현 이 있 기 때문에 반드시 운영 체제 로 밑바닥 의 조작 을 완성 해 야 한다.그렇다면 첫 번 째 데 이 터 는 반드시 커 널 구역 을 거 쳐 야 한다.그러나 우리 의 코드 는 사용자 구역 에 달 려 있 습 니 다.그러면 일반적인 상황 에서 커 널 데이터 가 존재 하고 사용자 구역 의 데 이 터 를 복사 하 는 과정 이 있 습 니 다.이것 은 읽 는 과정 이 고 쓰 는 과정 은 반대 되 는 조작 이다.사용자 구역 에서 데 이 터 를 커 널 구역 으로 복사 한 다음 에 커 널 에서 io 작업 을 완성 한다.
메모 리 를 커 널 과 사용자 구역 으로 직접 나 누 는 것 은 너무 광범 위 해서 틀 릴 수 없 지만 말 하지 않 은 것 과 같은 느낌 이 듭 니 다.
따라서 메모리 에 대한 구분 은 메모리 모델 이나 메모리 영역 이라는 것 을 좀 더 세분 화해 야 한다.각 언어의 각 장면 이 각자 실현 되 는 것 은 자연히 백가쟁명 이 므 로 크게 나 무 랄 것 이 없다.그러나 대체적으로 일정한 규칙 에 따라 서로 다른 용도 의 구역 으로 나 눈 다음 에 필요 할 때 이 구역 에 메모리 분 배 를 하고 해당 하 는 표 나 표지 에 저장 하여 나중에 읽 을 수 있 거나 재배 치 할 수 없 도록 한다.그 중에서 도 중요 한 것 은 메모 리 를 어떻게 분배 하 는 지 아 는 것 외 에 메모 리 를 어떻게 회수 하 는 지 아 는 것 이다.또한 메모리 의 가시 성 을 어떻게 확보 하 는 지도 메모리 모델 이 고려 해 야 할 중요 한 화제 이다.
구체 적 으로 실현 되 는 것 은 말 할 필요 가 없다.왜냐하면 온 세상 에 내 버 려 두 는 것 이 모두 정확 하 다 는 말 이 없 기 때문에 나 도 이 일 을 분명하게 말 할 능력 이 없다.모두 스스로 뇌 보 합 시다.
2.자바 의 직접 메모리 원리
우선 자바 에는 왜 직접 메모리 라 는 개념 이 있 습 니까?우 리 는 자바 에 매우 중요 한 메모리 구역 이 있다 는 것 을 알 고 있 습 니 다.즉,메모 리 를 쌓 는 것 입 니 다.거의 모든 대상 이 쌓 여 분 배 를 하기 때문에 대부분의 GC 작업 도 쌓 기 를 위 한 것 입 니 다.지난 절 에 말 한 것 과 관련 하여 메모 리 를 쌓 으 면 사용자 공간 메모리 영역 으로 나 눌 수 있 습 니 다.자바 는 이 메모리 만 잘 관리 하면 기본적으로 자바 의 대상 의 생명 주 기 를 잘 관리 할 수 있다 고 해 야 한다.그렇다면 도대체 무슨 직접 메모리 입 니까?메모리 더미 와 무슨 관계 가 있 습 니까?
직접 메모 리 는 쌓 인 공간 에서 벗 어 나 자바 의 쌓 인 것 이 아니 라 다른 지역 도 속 하지 않 습 니 다.즉,직접 메모 리 는 jvm 의 통 제 를 받 지 않 습 니 다.그것 은 시스템 이 직접 제어 하 는 메모리 영역 에 속한다.
왜 직접 메모리 가 jvm 의 통제 에서 벗 어 나 야 합 니까?jvm 이 통제 하 는 것 은 사용자 공간 이 고 어떤 장면 은 커 널 공간의 개입 이 있어 야 전체 과정 이 완성 된다.사용자 공간 이 데 이 터 를 가 져 오 려 면 커 널 에서 데 이 터 를 복사 해 야 사용자 공간 을 볼 수 있 습 니 다.많은 이런 장면 에서 데 이 터 를 복제 하 는 목적 은 단지 한 번 의 데 이 터 를 사용 하기 위해 해당 하 는 전환 을 한 후에 관계 가 있 는 것 을 사용 하지 않 는 다.예 를 들 어 스 트림 데이터 의 접속 과정 이다.이 복사 과정 은 반드시 많은 성능 손실 이 있 기 때문에 직접 메모리 가 나타난다.커 널 공간 과 사용자 공간 간 의 무의미 한 데이터 복 제 를 피하 고 프로그램의 성능 을 향상 시 키 는 것 이 목적 이다.
직접 메모 리 는 jvm 의 통 제 를 받 지 않 습 니 다.그러면 그것 은 누구의 통 제 를 받 습 니까?실제로 운영 체제 의 바 텀 에서 관리 하고 제어 하 며 메모리 배분 요청 을 할 때 시스템 은 공유 구역 을 신청 합 니 다.커 널 과 사용자 코드 가 이곳 의 데 이 터 를 공유 하여 기록 합 니 다.즉,커 널 이 기록 한 데 이 터 는 사용자 코드 가 직접 접근 할 수 있 고 사용자 코드 가 기록 한 데 이 터 는 커 널 이 직접 사용 할 수 있 습 니 다.밑바닥 에 서 는 mmap 라 는 함수 인터페이스 로 이 루어 진 공유 메모리 입 니 다.
자바 차원 에 서 는 DirectByteBuffer 를 사용 하여 보 여 줍 니 다.생 성,사용,삭 제 는 다음 과 같 습 니 다.

//           
    ByteBuffer buffer = ByteBuffer.allocateDirect(1600);
    for (int i = 0; i < 90_0000; i++) {
        for (int j = 0; j < 199; j++) {
            //      
            buffer.putInt(j);
        }
        buffer.flip();
        for (int j = 0; j < 199; j++) {
            //      
            buffer.get();
        }
        //     
        buffer.clear();
    }
3.Netty 에서 직접 메모리 사용
직접 메모리 의 사용 과정 을 알 게 되면 더 좋 은 장면 을 어떻게 찾 는 지 는 우리 가 발견 해 야 한다.netty 는 고성능 네트워크 통신 프레임 워 크 로 서 중요 한 작업 은 네트워크 io 문 제 를 처리 하 는 것 이다.그렇다면 그 장면 에서 직접 메모리 라 는 살 기 를 사용 하 는 것 은 더 할 나 위 없 이 좋다.그렇다면 넷 티 는 이 를 어떻게 이용 하 는 것 일 까?
두 장면:1.응용 에 네트워크 데 이 터 를 전달 할 때(읽 기 과정).2.원 격 으로 데 이 터 를 전달 할 때(쓰기 과정);

//    , msg               
    // io.netty.handler.codec.MessageToByteEncoder#write
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        ByteBuf buf = null;
        try {
            if (acceptOutboundMessage(msg)) {
                @SuppressWarnings("unchecked")
                I cast = (I) msg;
                //    preferDirect = true;
                buf = allocateBuffer(ctx, cast, preferDirect);
                try {
                    //        ,    ,        
                    encode(ctx, cast, buf);
                } finally {
                    ReferenceCountUtil.release(cast);
                }

                if (buf.isReadable()) {
                    //       
                    ctx.write(buf, promise);
                } else {
                    buf.release();
                    ctx.write(Unpooled.EMPTY_BUFFER, promise);
                }
                buf = null;
            } else {
                ctx.write(msg, promise);
            }
        } catch (EncoderException e) {
            throw e;
        } catch (Throwable e) {
            throw new EncoderException(e);
        } finally {
            if (buf != null) {
                buf.release();
            }
        }
    }
    // io.netty.handler.codec.MessageToByteEncoder#allocateBuffer
    /**
     * Allocate a {@link ByteBuf} which will be used as argument of {@link #encode(ChannelHandlerContext, I, ByteBuf)}.
     * Sub-classes may override this method to return {@link ByteBuf} with a perfect matching {@code initialCapacity}.
     */
    protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, @SuppressWarnings("unused") I msg,
                               boolean preferDirect) throws Exception {
        if (preferDirect) {
            // PooledByteBufAllocator
            return ctx.alloc().ioBuffer();
        } else {
            return ctx.alloc().heapBuffer();
        }
    }
    // io.netty.buffer.AbstractByteBufAllocator#ioBuffer()
    @Override
    public ByteBuf ioBuffer() {
        if (PlatformDependent.hasUnsafe()) {
            return directBuffer(DEFAULT_INITIAL_CAPACITY);
        }
        return heapBuffer(DEFAULT_INITIAL_CAPACITY);
    }
    // io.netty.buffer.AbstractByteBufAllocator#directBuffer(int)
    @Override
    public ByteBuf directBuffer(int initialCapacity) {
        return directBuffer(initialCapacity, DEFAULT_MAX_CAPACITY);
    }
    @Override
    public ByteBuf directBuffer(int initialCapacity, int maxCapacity) {
        if (initialCapacity == 0 && maxCapacity == 0) {
            return emptyBuf;
        }
        validate(initialCapacity, maxCapacity);
        return newDirectBuffer(initialCapacity, maxCapacity);
    }
    // io.netty.buffer.PooledByteBufAllocator#newDirectBuffer
    @Override
    protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
        PoolThreadCache cache = threadCache.get();
        PoolArena<ByteBuffer> directArena = cache.directArena;

        final ByteBuf buf;
        if (directArena != null) {
            buf = directArena.allocate(cache, initialCapacity, maxCapacity);
        } else {
            buf = PlatformDependent.hasUnsafe() ?
                    UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity) :
                    new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
        }

        return toLeakAwareBuffer(buf);
    }

    // io.netty.buffer.PoolArena#allocate(io.netty.buffer.PoolThreadCache, int, int)
    PooledByteBuf<T> allocate(PoolThreadCache cache, int reqCapacity, int maxCapacity) {
        PooledByteBuf<T> buf = newByteBuf(maxCapacity);
        allocate(cache, buf, reqCapacity);
        return buf;
    }
        // io.netty.buffer.PoolArena.DirectArena#newByteBuf
        @Override
        protected PooledByteBuf<ByteBuffer> newByteBuf(int maxCapacity) {
            if (HAS_UNSAFE) {
                return PooledUnsafeDirectByteBuf.newInstance(maxCapacity);
            } else {
                return PooledDirectByteBuf.newInstance(maxCapacity);
            }
        }

    private void allocate(PoolThreadCache cache, PooledByteBuf<T> buf, final int reqCapacity) {
        final int normCapacity = normalizeCapacity(reqCapacity);
        if (isTinyOrSmall(normCapacity)) { // capacity < pageSize
            int tableIdx;
            PoolSubpage<T>[] table;
            boolean tiny = isTiny(normCapacity);
            if (tiny) { // < 512
                if (cache.allocateTiny(this, buf, reqCapacity, normCapacity)) {
                    // was able to allocate out of the cache so move on
                    return;
                }
                tableIdx = tinyIdx(normCapacity);
                table = tinySubpagePools;
            } else {
                if (cache.allocateSmall(this, buf, reqCapacity, normCapacity)) {
                    // was able to allocate out of the cache so move on
                    return;
                }
                tableIdx = smallIdx(normCapacity);
                table = smallSubpagePools;
            }

            final PoolSubpage<T> head = table[tableIdx];

            /**
             * Synchronize on the head. This is needed as {@link PoolChunk#allocateSubpage(int)} and
             * {@link PoolChunk#free(long)} may modify the doubly linked list as well.
             */
            synchronized (head) {
                final PoolSubpage<T> s = head.next;
                if (s != head) {
                    assert s.doNotDestroy && s.elemSize == normCapacity;
                    long handle = s.allocate();
                    assert handle >= 0;
                    s.chunk.initBufWithSubpage(buf, handle, reqCapacity);
                    incTinySmallAllocation(tiny);
                    return;
                }
            }
            synchronized (this) {
                allocateNormal(buf, reqCapacity, normCapacity);
            }

            incTinySmallAllocation(tiny);
            return;
        }
        if (normCapacity <= chunkSize) {
            if (cache.allocateNormal(this, buf, reqCapacity, normCapacity)) {
                // was able to allocate out of the cache so move on
                return;
            }
            synchronized (this) {
                allocateNormal(buf, reqCapacity, normCapacity);
                ++allocationsNormal;
            }
        } else {
            // Huge allocations are never served via the cache so just call allocateHuge
            allocateHuge(buf, reqCapacity);
        }
    }
    // io.netty.util.internal.PlatformDependent0#newDirectBuffer
    static ByteBuffer newDirectBuffer(long address, int capacity) {
        ObjectUtil.checkPositiveOrZero(capacity, "capacity");

        try {
            return (ByteBuffer) DIRECT_BUFFER_CONSTRUCTOR.newInstance(address, capacity);
        } catch (Throwable cause) {
            // Not expected to ever throw!
            if (cause instanceof Error) {
                throw (Error) cause;
            }
            throw new Error(cause);
        }
    }
ByteBuffer 에 데 이 터 를 기록 하 는 과정,즉 직접 메모리 에 데 이 터 를 기록 하 는 과정 입 니 다.일반적인 대상 처럼 간단 하지 않 을 수도 있 습 니 다.

// io.netty.buffer.AbstractByteBuf#writeBytes(byte[])
    @Override
    public ByteBuf writeBytes(byte[] src) {
        writeBytes(src, 0, src.length);
        return this;
    }

    @Override
    public ByteBuf writeBytes(byte[] src, int srcIndex, int length) {
        ensureWritable(length);
        setBytes(writerIndex, src, srcIndex, length);
        writerIndex += length;
        return this;
    }
    
    // io.netty.buffer.PooledUnsafeDirectByteBuf#setBytes(int, byte[], int, int)
    @Override
    public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {
        // addr()           
        UnsafeByteBufUtil.setBytes(this, addr(index), index, src, srcIndex, length);
        return this;
    }
    // io.netty.buffer.PooledUnsafeDirectByteBuf#addr
    private long addr(int index) {
        return memoryAddress + index;
    }

    // io.netty.buffer.UnsafeByteBufUtil#setBytes(io.netty.buffer.AbstractByteBuf, long, int, byte[], int, int)
    static void setBytes(AbstractByteBuf buf, long addr, int index, byte[] src, int srcIndex, int length) {
        buf.checkIndex(index, length);
        if (length != 0) {
            //      copy DirectByteBuffer 
            PlatformDependent.copyMemory(src, srcIndex, addr, length);
        }
    }
    // io.netty.util.internal.PlatformDependent#copyMemory(byte[], int, long, long)
    public static void copyMemory(byte[] src, int srcIndex, long dstAddr, long length) {
        PlatformDependent0.copyMemory(src, BYTE_ARRAY_BASE_OFFSET + srcIndex, null, dstAddr, length);
    }
    // io.netty.util.internal.PlatformDependent0#copyMemory(java.lang.Object, long, java.lang.Object, long, long)
    static void copyMemory(Object src, long srcOffset, Object dst, long dstOffset, long length) {
        //UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, length);
        while (length > 0) {
            long size = Math.min(length, UNSAFE_COPY_THRESHOLD);
            //    jvm     ,     copy,   dst null,      copy     dstOffset  
            //       :        ARRAY_OBJECT_BASE_OFFSET...
            UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, size);
            length -= size;
            srcOffset += size;
            dstOffset += size;
        }
    }
마지막 으로 직접 메모리 의 기록 은 Unsafe 류 를 통 해 운영 체제 에 메모리 데 이 터 를 기록 하 는 것 을 볼 수 있 습 니 다.
마지막 으로 원 격 으로 데 이 터 를 쓰 는 방법 을 보 겠 습 니 다.

// io.netty.channel.AbstractChannelHandlerContext#write(java.lang.Object, io.netty.channel.ChannelPromise)
    @Override
    public ChannelFuture write(final Object msg, final ChannelPromise promise) {
        if (msg == null) {
            throw new NullPointerException("msg");
        }

        try {
            if (isNotValidPromise(promise, true)) {
                ReferenceCountUtil.release(msg);
                // cancelled
                return promise;
            }
        } catch (RuntimeException e) {
            ReferenceCountUtil.release(msg);
            throw e;
        }
        write(msg, false, promise);

        return promise;
    }

    private void write(Object msg, boolean flush, ChannelPromise promise) {
        AbstractChannelHandlerContext next = findContextOutbound();
        final Object m = pipeline.touch(msg, next);
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            if (flush) {
                next.invokeWriteAndFlush(m, promise);
            } else {
                next.invokeWrite(m, promise);
            }
        } else {
            AbstractWriteTask task;
            if (flush) {
                task = WriteAndFlushTask.newInstance(next, m, promise);
            }  else {
                task = WriteTask.newInstance(next, m, promise);
            }
            safeExecute(executor, task, promise, m);
        }
    }

    private void invokeWrite(Object msg, ChannelPromise promise) {
        if (invokeHandler()) {
            invokeWrite0(msg, promise);
        } else {
            write(msg, promise);
        }
    }
    
    private void invokeWrite0(Object msg, ChannelPromise promise) {
        try {
            ((ChannelOutboundHandler) handler()).write(this, msg, promise);
        } catch (Throwable t) {
            notifyOutboundHandlerException(t, promise);
        }
    }
        // io.netty.channel.DefaultChannelPipeline.HeadContext#write
        @Override
        public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
            unsafe.write(msg, promise);
        }
        // io.netty.channel.AbstractChannel.AbstractUnsafe#write
        @Override
        public final void write(Object msg, ChannelPromise promise) {
            assertEventLoop();

            ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
            if (outboundBuffer == null) {
                // If the outboundBuffer is null we know the channel was closed and so
                // need to fail the future right away. If it is not null the handling of the rest
                // will be done in flush0()
                // See https://github.com/netty/netty/issues/2362
                safeSetFailure(promise, WRITE_CLOSED_CHANNEL_EXCEPTION);
                // release message now to prevent resource-leak
                ReferenceCountUtil.release(msg);
                return;
            }

            int size;
            try {
                //   msg     ,    
                msg = filterOutboundMessage(msg);
                size = pipeline.estimatorHandle().size(msg);
                if (size < 0) {
                    size = 0;
                }
            } catch (Throwable t) {
                safeSetFailure(promise, t);
                ReferenceCountUtil.release(msg);
                return;
            }
            //  msg  outboundBuffer ,         
            outboundBuffer.addMessage(msg, size, promise);
        }
    // io.netty.channel.nio.AbstractNioByteChannel#filterOutboundMessage
    @Override
    protected final Object filterOutboundMessage(Object msg) {
        if (msg instanceof ByteBuf) {
            ByteBuf buf = (ByteBuf) msg;
            if (buf.isDirect()) {
                return msg;
            }

            return newDirectBuffer(buf);
        }

        if (msg instanceof FileRegion) {
            return msg;
        }

        throw new UnsupportedOperationException(
                "unsupported message type: " + StringUtil.simpleClassName(msg) + EXPECTED_TYPES);
    }
    // io.netty.channel.ChannelOutboundBuffer#addMessage
    /**
     * Add given message to this {@link ChannelOutboundBuffer}. The given {@link ChannelPromise} will be notified once
     * the message was written.
     */
    public void addMessage(Object msg, int size, ChannelPromise promise) {
        Entry entry = Entry.newInstance(msg, size, total(msg), promise);
        if (tailEntry == null) {
            flushedEntry = null;
        } else {
            Entry tail = tailEntry;
            tail.next = entry;
        }
        tailEntry = entry;
        if (unflushedEntry == null) {
            unflushedEntry = entry;
        }

        // increment pending bytes after adding message to the unflushed arrays.
        // See https://github.com/netty/netty/issues/1619
        //     ,     fireChannelWritabilityChanged   ,            
        incrementPendingOutboundBytes(entry.pendingSize, false);
    }
아마도 직접 메모리 로 작 성 된 데 이 터 는 커 널 의 접속 인 터 페 이 스 를 다시 호출 하여 직접 메모리 의 데 이 터 를 버퍼 에 넣 으 면 원 격 으로 보 낼 수 있 을 것 이다.
마지막 으로 네트워크 데이터 의 접속 읽 기 과정 을 간략하게 살 펴 보고 직접 메모 리 를 사 용 했 는 지,어떻게 사 용 했 는 지 판별 합 니 다.

// io.netty.channel.nio.AbstractNioByteChannel.NioByteUnsafe#read
        @Override
        public final void read() {
            final ChannelConfig config = config();
            final ChannelPipeline pipeline = pipeline();
            final ByteBufAllocator allocator = config.getAllocator();
            final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
            allocHandle.reset(config);

            ByteBuf byteBuf = null;
            boolean close = false;
            try {
                do {
            //     ByteBuffer,              
                    byteBuf = allocHandle.allocate(allocator);
            //       ByteBuffer 
                    allocHandle.lastBytesRead(doReadBytes(byteBuf));
                    if (allocHandle.lastBytesRead() <= 0) {
                        // nothing was read. release the buffer.
                        byteBuf.release();
                        byteBuf = null;
                        close = allocHandle.lastBytesRead() < 0;
                        break;
                    }

                    allocHandle.incMessagesRead(1);
                    readPending = false;
            //         ,  pipeline     ,          
                    pipeline.fireChannelRead(byteBuf);
                    byteBuf = null;
                } while (allocHandle.continueReading());

                allocHandle.readComplete();
                pipeline.fireChannelReadComplete();

                if (close) {
                    closeOnRead(pipeline);
                }
            } catch (Throwable t) {
                handleReadException(pipeline, byteBuf, t, close, allocHandle);
            } finally {
                // Check if there is a readPending which was not processed yet.
                // This could be for two reasons:
                // * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method
                // * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method
                //
                // See https://github.com/netty/netty/issues/2254
                if (!readPending && !config.isAutoRead()) {
                    removeReadOp();
                }
            }
        }
    }
    // io.netty.channel.DefaultMaxMessagesRecvByteBufAllocator.MaxMessageHandle#allocate
        @Override
        public ByteBuf allocate(ByteBufAllocator alloc) {
            return alloc.ioBuffer(guess());
        }
    // io.netty.buffer.AbstractByteBufAllocator#ioBuffer(int)
    @Override
    public ByteBuf ioBuffer(int initialCapacity) {
        if (PlatformDependent.hasUnsafe()) {
            return directBuffer(initialCapacity);
        }
        return heapBuffer(initialCapacity);
    }
이 를 통 해 알 수 있 듯 이 데 이 터 를 접속 할 때 직접 메모리 로 데 이 터 를 수신 하여 커 널 과 사용자 가 공유 하고 복사 할 필요 가 없 는 목적 을 달성 할 수 있다.
이상 은 netty 가 전체 직접 메모리 에 대한 조작 방식 입 니 다.좀 복잡 해 보이 는데 주로 netty 는 디자인 철학의 표현 이 곳곳에 있 습 니 다.사건 을 쓰 거나 사건 을 읽 거나 상태 변경 사건 이 든 모두 긴 라인 작업 입 니 다.당연 하지,우리 가 여기에서 토론 한 것 은 직접 메모 리 를 어떻게 사용 하 는 지 하 는 것 이다.이것 은 PooledUnsafeDirectByteBuf 를 사용 하여 jdk 의 direct=ByteBuffer.allocateDirect(1)를 참조 합 니 다.DirectByteBuffer 를 사용 하여 직접 메모리 사용 을 실현 합 니 다.그리고 그 구조 방법 인 DirectByteBuffer(long addr,int cap)를 사용 하여 직접 메모리 대상 을 만 듭 니 다.
총화
전체적으로 볼 때 직접 메모 리 는 io 를 진행 할 때의 메모리 복사 체 조 를 줄 였 지만 커 널 과 사용자 공간의 메모리 복사 만 을 위 한 것 입 니 다.사용자 공간의 데이터 복사 가 적지 않 기 때 문 입 니 다.최종 적 으로 바 이 너 리 스 트림 으로 전환 해 야 서로 다른 공간의 프로그램 에서 읽 을 수 있 기 때 문 입 니 다.그러나 직접 메모리 대상 을 만 드 는 비용 은 일반 메모리 대상 을 만 드 는 것 보다 높 습 니 다.더 복잡 한 관계 환경 을 유지 해 야 할 수도 있 기 때 문 입 니 다.사실,직접 메모 리 는 서로 다른 프로 세 스 간 의 메모리 공 유 를 할 수 있 습 니 다.이것 은 일반 대상 메모리 에서 할 수 없습니다.자바 의 직접 메모리 사용 은 시스템 에 제공 하 는 편리 한 인터페이스 만 사용 하여 더 좋 은 장면 에 적응 합 니 다.
직접 메모 리 는 실제로 공유 메모리 라 고도 할 수 있 습 니 다.서로 다른 프로 세 스 가 이 블록 메모리 주소 에 대한 수정 을 볼 수 있 습 니 다.이것 은 효율 적 인 프로 세 스 간 통신 방식 으로 다 중 프로 세 스 응용 에 도움 이 된다.그러나 다 중 스 레 드 응용 은 필수 가 아 닙 니 다.다 중 스 레 드 자체 가 공유 메모리 이기 때 문 입 니 다.nginx 와 같은 응용 은 매우 유용 하 다.일부 전역 카운터 에 대해 서 는 다 중 프로 세 스 유지 가 필요 하기 때문에 공유 메모리 로 완벽 하 게 해결 해 야 합 니 다.
한편,netty 는 네트워크 통신 구조 로 서 구체 적 인 장면 을 잘 처리 하고 직접 메모 리 를 합 리 적 으로 사용 하여 이른바 제로 복사,고성능 의 초석 중 하 나 를 이 루 었 다.그래서 좋 은 틀 은 반드시 특정한 문 제 를 해결 하 는 훌륭 한 틀 이다.이것 은 반드시 기능 의 창시자 가 아니 라 좋 은 계승자 가 될 것 이다.
또 메모리 관 리 는 매우 복잡 한 문제 다.하지만 중요 한 것 은 우리 가 많은 시간 을 들 여 연구 할 만하 다.
이상 은 Netty 직접 메모리 원리 와 응용 에 대한 상세 한 내용 을 분석 하 는 것 입 니 다.Netty 직접 메모리 원리 에 관 한 자 료 는 다른 관련 글 에 주목 하 십시오!

좋은 웹페이지 즐겨찾기