의의 Netty 소스 코드 분석 ByteBuf

의의 Netty 소스 코드 분석 ByteBuf


기초 ByteBuf

자바 니오의 버퍼

프로세스 데이터 전송 동안, 우리는 종종 버퍼를 사용합니다.
자바 NIO 일곱 기본 버퍼를 우리에게 제공에서 자바의 일곱 가지 기본 유형에 해당하는 달성했다. 일반적으로 더의 ByteBuffer를 사용합니다. 기본 버퍼는 우리의 일상적인 사용을 충족하지만, 정말 조금 힘, 기본 버퍼는 다음과 같은 단점이있을 때 복잡한 응용 프로그램을 수행 할 수 있지만. 우리의 사용하기에 더 친화적 인 인터페이스를 제공 그러므로 인 Netty 포장.

  • 우리가 대응하는 방법은 버퍼 인스턴스를 만들 버퍼 클래스를 할당 호출하면, 버퍼의 길이가 고정 될 때, 지정된 공간을 할당하고 동적으로 확장하거나 축소 할 수 없습니다. 우리는 데이터를 작성하는 경우가 배열 범위 오류가 발생합니다 버퍼의 용량보다 더 크다.
  • 버퍼는 하나 개의 위치 플래그, 우리는 플립 또는 데이터 액세스 위치를 처리 할 수있는 위치를 수정하는 되감기 방식으로 수, 위치 속성이 오류가 발생할 수 있습니다 생각합니다.
  • 버퍼 전용 액세스, 플립, 릴리스, 기호, 비교, 대량 이동과 버퍼의 기타 기본 작업을 제공합니다, 우리는 고급 기능을 사용하려면, 당신은 소유하고 수동으로 패키지를 유지, 매우 불편 사용해야합니다.

작품 ByteBuf

ByteBuf 또한 바이트 배열을 통해 데이터를 액세스하기위한 버퍼로서, 외형 패턴에서의 ByteBuffer JDK NIO 소자 캡슐화 중합.
ByteBuf은 읽기에 도움과 readerIndex writerIndex이 개 위치 포인터 조작으로 버퍼를 작성하는 것입니다.
판독과 같은 개체가 0 writerIndex readerIndex 값의 초기화 시간이 경과하면 기록 동작 및 readerIndex writerIndex 0 readerIndex 사이에, 판독 동작 동안, writerIndex readerIndex 초과 공간을 증가 않겠지 폐기로 처리되며 discardReadBytes 방법 ByteBuf 전화, 압축 동작의 ByteBuffer 버퍼의 압축 유사한 공간의 일부를 재사용하도록 해제 될 수있다. 공간에 readerIndex writerIndex은 상기의 ByteBuffer 한계 위치에 대응하는 공간, 공간 WriterIndex 용량을 판독 할 수 있으며,의 ByteBuffer의 용량 한계에 공간 동등한 기록 될 계속할 수있다.
writerIndex 위치 포인터 readerIndex 읽고 분리 버퍼의 동작을 단순화 판독 및 기록하기 위해, 위치 포인터를 조정할 필요가 없도록 작업 물품.
우리는 버퍼에 쓰기 작업을 할 때 마찬가지로, 읽고 패키지에 쓰기 작업을 ByteBuf, 동적으로 확장 할 수있는 기능을 제공, 나머지 공간은 사용 가능한 공간이 부족하면 단어가 기록 될 때, 확인해야 할 필요가 섹션들의 수는 기록 가능한 최대 바이트 수보다 작은, 버퍼는 동적 버퍼가 다시 생성하고, 새롭게 생성 전에 버퍼로 데이터를 복사한다 확장 될

기본 기능 ByteBuf

  • 연속 읽기
    이전에 판독 동작을 검증 가능한 제 1 버퍼 공간. 판독 될 바이트 길이가 0보다 작 으면, 판독 될 바이트의 바이트 길이를 IndexOutOfBoundsException이 발생, 서면의 길이보다 큰 경우는 IllegalArgumentException 예외가 발생된다. getBytes 해당 서브 클래스에 의해 구현 될 수있는 추상적 방식이며, 동일하지 않은 검사를 통과 한 후, 현재 readerIndex부터 getBytes 메소드를 호출 인해 달성 다른 서브 클래스, DST의 목표 길이 바이트의 데이터 길이를 읽는다. 데이터가 성공적으로 읽을 경우, readerIndex의 길이는 상응하는 증가 될 것입니다.
public ByteBuf readBytes(ByteBuf dst, int dstIndex, int length) {
    checkReadableBytes(length);
    getBytes(readerIndex, dst, dstIndex, length);
    readerIndex += length;
    return this;
}
protected final void checkReadableBytes(int minimumReadableBytes) {
    ensureAccessible();
    if (minimumReadableBytes < 0) {
        throw new IllegalArgumentException("minimumReadableBytes: " + minimumReadableBytes + " (expected: >= 0)");
    }
    if (readerIndex > writerIndex - minimumReadableBytes) {
        throw new IndexOutOfBoundsException(String.format(
                "readerIndex(%d) + length(%d) exceeds writerIndex(%d): %s",
                readerIndex, minimumReadableBytes, writerIndex, this));
    }
}
  • 연속 기록
    , 판독 동작은 데이터 ByteBuf 길이의 현재 길이로 기록되며 srcIndex에서 소스 바이트 배열을 시작한다.
    쓰기 길이는, IllegalArgumentException가 예외가 발생합니다, 0보다 작은 경우 배열에 기록 된 바이트의 시작 번호는 확인이 필요한 경우 테스트를 통해, ByteBuf 기록 된 바이트의 현재 수보다 적을 수 있습니다 기입해진 바이트 수. 버퍼에 기록 된 바이트의 수는 용량 MAXCAPACITY의 최대 동적 확장보다 큰 경우 발생합니다
    그렇지 않으면 그들은 동적 확장에 의해 쓰여진 바이트 수를 충족해야합니다, IndexOutOfBoundsException이 있습니다. 재 전개 후 처음 용량에 의해 산출 CalculateNewCapacity 후, 상이한 서브 클래스는 다른 구현을 가지고 또한 추상적 방법 증설 메소드를 호출한다.
    • 증설 컴퓨팅, 제 1 게이트 밸브는, 4m 제공된다 새로운 버퍼 임계 용량이 동일한 임계 값을 사용하는 능력을 확장하고자하는 경우는, 4M, 4M 단위로하는 단계로 임계치보다 큰 경우 상기 확장 구간의 경우, 최대 확장 용량보다 더 큰 용량을 확장하기 위해, 다음은 최대 용량 MAXCAPACITY 새로운 용량을 확장 할 수 있습니다. 그렇지 않으면, 승산의 결과는 확장 할 수있는 용량보다 클 때까지, 64 배에서 시작하고 새로운 버퍼 용량의 결과.
    • 우리가 버퍼로 새로운 용량의 길이의 단지 writerIndex + 값은 각 시간 증설 필요성 후에 수행되는 기록 동작이 증설 프로세스 메모리 복제를 필요로 할 때 다시 경우, 첫 번째 단계를 두배로 용량을 확대 첫 번째 공간이 상대적으로 작은 메모리 복제 성능 저하를 일으킬 수 있음을 너무 많이, 이유는 다시 두 배로 장관은, 배 및 운영 폐기물은 너무 많은 메모리,하지만 특정 시간에 메모리 증가를 가져오고되지 않습니다 배가 시간, 따라서,이 임계에 도달 한 후 오랜 단계 접근법을 통해 평활화 성장에 임계 값을 설정해야하는 메모리의 낭비를 초래할 것이다.
public ByteBuf writeBytes(byte[] src, int srcIndex, int length) {
    ensureWritable(length);
    setBytes(writerIndex, src, srcIndex, length);
    writerIndex += length;
    return this;
}
public ByteBuf ensureWritable(int minWritableBytes) {
    if (minWritableBytes < 0) {
        throw new IllegalArgumentException(String.format(
                "minWritableBytes: %d (expected: >= 0)", minWritableBytes));
    }

    if (minWritableBytes <= writableBytes()) {
        return this;
    }

    if (minWritableBytes > maxCapacity - writerIndex) {
        throw new IndexOutOfBoundsException(String.format(
                "writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",
                writerIndex, minWritableBytes, maxCapacity, this));
    }

    // Normalize the current capacity to the power of 2.
    int newCapacity = calculateNewCapacity(writerIndex + minWritableBytes);

    // Adjust to the new capacity.
    capacity(newCapacity);
    return this;
}
private int calculateNewCapacity(int minNewCapacity) {
    final int maxCapacity = this.maxCapacity;
    final int threshold = 1048576 * 4; // 4 MiB page

    if (minNewCapacity == threshold) {
        return threshold;
    }

    // If over threshold, do not double but just increase by threshold.
    if (minNewCapacity > threshold) {
        int newCapacity = minNewCapacity / threshold * threshold;
        if (newCapacity > maxCapacity - threshold) {
            newCapacity = maxCapacity;
        } else {
            newCapacity += threshold;
        }
        return newCapacity;
    }

    // Not over threshold. Double up to 4 MiB, starting from 64.
    int newCapacity = 64;
    while (newCapacity < minNewCapacity) {
        newCapacity <<= 1;
    }

    return Math.min(newCapacity, maxCapacity);
}
//UnpooledHeapByteBuf的capacity实现
public ByteBuf capacity(int newCapacity) {
    ensureAccessible();
    if (newCapacity < 0 || newCapacity > maxCapacity()) {
        throw new IllegalArgumentException("newCapacity: " + newCapacity);
    }

    int oldCapacity = array.length;
    if (newCapacity > oldCapacity) {
        byte[] newArray = new byte[newCapacity];
        System.arraycopy(array, 0, newArray, 0, array.length);
        setArray(newArray);
    } else if (newCapacity < oldCapacity) {
        byte[] newArray = new byte[newCapacity];
        int readerIndex = readerIndex();
        if (readerIndex < newCapacity) {
            int writerIndex = writerIndex();
            if (writerIndex > newCapacity) {
                writerIndex(writerIndex = newCapacity);
            }
            System.arraycopy(array, readerIndex, newArray, readerIndex, writerIndex - readerIndex);
        } else {
            setIndex(newCapacity, newCapacity);
        }
        setArray(newArray);
    }
    return this;
}
  • 지우기 동작을
    명확 운영 및 writerIndex readerIndex 단지를 0으로 설정, 저장된 데이터를 수정되지 않습니다.
public ByteBuf clear() {
    readerIndex = writerIndex = 0;
    return this;
}
  • 인덱스 작업

    • 기록 위치 인덱스 제공 : 주로 경계 조건, 0 이상으로 readerIndex, newReaderIndex 설정할 때 writerIndex보다 큰지 확인하기 위해, writerIndex 제공 newWriterIndex readerIndex의 전류 용량보다 더 작아야한다. 확인할 수없는 경우 IndexOutOfBoundsException이를 던질 것입니다.
    • 마크와 리셋 동작 : 때문에 readerIndex 및 writerIndex 따라서 markedReaderIndex 또는 markedWriterIndex 마크 또는 작동 위치 인덱스를 지정해야 리셋 표시 동작 전류 readerIndex 또는 writerIndex 설정할 것이다 리셋 동작 후, 그 값에 대응하는 결합 마크는 해당 readerIndex () 또는 writerIndex ()을 호출;
  • 버퍼 재사용은
    이미 discardReadByte 버퍼 방법으로 읽은 재사용로 이동 할 수 있습니다.
    먼저 readerIndex 판사 :
    • 0이 readerIndex, 그것은 더 판독 데이터가 없음을 의미하는 경우에는 공간 재사용 직접 반환하는 데 사용되지 않을 수있다;
    • 0 같고 readerIndex writerIndex 이하인, 그것이 데이터 버퍼가 버려진가 판독되는 것을 나타내는 경우, 상기 버퍼는 아직 판독되지 않았다. 이 방법은 setBytes 바이트 어레이 복사 호출 readerIndex writerIndex 설정 다시 버퍼의 시작 부분으로 이동하고, 판독되지 데이터는 readerIndex는 0, 원래 writerIndex-readerIndex을 writerIndex, 또한 필요 마크 재설정.
      • 먼저, 감소와 비교하여 다음 markedReaderIndex의 백업을, markedReaderIndex 경우 작은 감분 단어보다 markedReaderIndex 후 미만이 markedWriterIndex은 또한 0으로 설정된 경우 0 감소와 다음 markedWriterIndex 비교에 달리 markedWriterIndex 적은 감소를 설정;
      • 단어의 감소보다 큰 markedReaderIndex 경우, markedReaderIndex 및 markedReaderIndex는 감소에게 그것을 뺀다.
    • readerIndex writerIndex 동일한 경우에는 버퍼가 재사용 될 수 없다 그것에 마크없이 메모리 복사에 직접 다시 설정 있다는 것을 나타낸다.
public ByteBuf discardReadBytes() {
    ensureAccessible();
    if (readerIndex == 0) {
        return this;
    }

    if (readerIndex != writerIndex) {
        setBytes(0, this, readerIndex, writerIndex - readerIndex);
        writerIndex -= readerIndex;
        adjustMarkers(readerIndex);
        readerIndex = 0;
    } else {
        adjustMarkers(readerIndex);
        writerIndex = readerIndex = 0;
    }
    return this;
}
protected final void adjustMarkers(int decrement) {
    int markedReaderIndex = this.markedReaderIndex;
    if (markedReaderIndex <= decrement) {
        this.markedReaderIndex = 0;
        int markedWriterIndex = this.markedWriterIndex;
        if (markedWriterIndex <= decrement) {
            this.markedWriterIndex = 0;
        } else {
            this.markedWriterIndex = markedWriterIndex - decrement;
        }
    } else {
        this.markedReaderIndex = markedReaderIndex - decrement;
        markedWriterIndex -= decrement;
    }
}
  • skipBytes

우리는 바이트의 일부를 생략 할 필요가있을 때, 당신은 데이터를 다시 읽어 메소드 호출 skipBytes에게 지정된 바이트 길이를 건너 뛸 수 있습니다.
스킵 길이가 0보다 작은 다음에 IllegalArgumentException가 발생하고, 현재의 버퍼 길이를 생략하여 IndexOutOfBoundsException를 던질 수있는 리드의 길이보다 큰 경우 우선 점프 길이가 결정된다. 체크는 writerIndex보다 새로운 readerIndex 큰, 그것은 IndexOutOfBoundsException이 발생합니다, 또는 readerIndex를 업데이트 할 경우, 새로운 readerindex 원래 readerIndex + 길이를 전달합니다.

public ByteBuf skipBytes(int length) {
    checkReadableBytes(length);
    int newReaderIndex = readerIndex + length;
    if (newReaderIndex > writerIndex) {
        throw new IndexOutOfBoundsException(String.format(
                "length: %d (expected: readerIndex(%d) + length <= writerIndex(%d))",
                length, readerIndex, writerIndex));
    }
    readerIndex = newReaderIndex;
    return this;
}

ByteBuf 소스 코드 분석

ByteBuf

AbstractReferenceCountedByteBuf

AbstractReferenceCountedByteBuf은 할당 및 파괴 목적, 자동 메모리 교정을 추적하는 데 사용되는 참조 카운팅 기본 클래스 ByteBuf 달성된다.

  • 멤버 변수
    • refCntUpdater refCntUpdater AtomicIntegerFieldUpdater 타입의 멤버 변수가 그 스레드 안전을 보장 멤버 변수에 원자 업데이트 작업이 될 수있다.
    • REFCNT_FIELD_OFFSET REFCNT_FIELD_OFFSET이 상기 UnpooledDirectByteBuf PooledDirectByteBuf 두 개의 서브 클래스를 상쇄하기 위해 사용되며, refCnt AbstractReferenceCountedByteBuf 메모리 어드레스 필드를 식별한다.
    • refCnt 휘발성 변수를 추적하는데 사용되는 실의 가시성을 보장하기 위해 변경되는 서지 개체
  • 개체 참조 카운터는
    참조 카운터를 유지하기 위해 모든 메소드 호출은 1 씩 증가합니다. 프로그램이 정확하게 실행하는 것에는 최소값이 될 때 애플리케이션과 동일한 횟수와 시간 ByteBuf 대응한다 해제해야만큼, 스핀 동작에 의해 참조 카운터는 기준 카운터의 초기 값을 가산하는 방법을 유지 그것은 재활용된다. 숫자 오브젝트가 실수로 인용되었음을 나타내는 0이면 정수 타입의 최대 값과 동일한 횟수, 던질 것인지, IllegalReferenceCountException는 예외를 발생
    IllegalReferenceCountException 예외. 상기 방법은 원자 갱신 동작을 유지의 compareAndSet refCntUpdater 수행하고 값의 compareAndSet는 다음의 비교가 다시 회전 실패 비교 소자 경우 변수를 수정할 수있는 다른 스레드가, 취득 된 기대치를 비교하고, 다시 기준 카운터의 값을 취득하여 이용한다 스핀을 종료, 그렇지 않으면 플러스가 될 것입니다, 비교.
    유사 박리 방법, 방법을 유지할뿐만 아니라 판단하고, 회전주기에 갱신되지만 refCnt의 값은 애플리케이션이 도달 된 개체 참조로서 릴리스 번호 카운터를 참조하는 것을 나타내는 1과 같을 때, 객체는 가비지되어야 재활용 수거 아웃, 할당 해제 호출 방법은 객체를 출시 ByteBuf
public ByteBuf retain() {
    for (;;) {
        int refCnt = this.refCnt;
        if (refCnt == 0) {
            throw new IllegalReferenceCountException(0, 1);
        }
        if (refCnt == Integer.MAX_VALUE) {
            throw new IllegalReferenceCountException(Integer.MAX_VALUE, 1);
        }
        if (refCntUpdater.compareAndSet(this, refCnt, refCnt + 1)) {
            break;
        }
    }
    return this;
}
    
public final boolean release() {
    for (;;) {
        int refCnt = this.refCnt;
        if (refCnt == 0) {
            throw new IllegalReferenceCountException(0, -1);
        }

        if (refCntUpdater.compareAndSet(this, refCnt, refCnt - 1)) {
            if (refCnt == 1) {
                deallocate();
                return true;
            }
            return false;
        }
    }
}

UnpooledHeapByteBuf

UnpooledHeapByteBuf 메모리 할당 또는 해제 될 경우 각 IO 동작에 영향을 자주 수행하는 UnpooledHeapByteBuf 객체를 생성하는, 힙 메모리 할당 바이트 버퍼를 달성하기 위해 비 - 스레드 풀이다.

  • 멤버 변수
    • 메모리 할당 ByteBufAllocator
    • 바이트의 데이터를 저장하는 바이트 배열 버퍼로서 배열
    • 의 ByteBuffer는 니오의 ByteBuffer를 변환의 Netty ByteBuf을 구현하는
  • 동적 확장 버퍼는
    제 새로운 용량의 크기가 최대 확장 용량 MAXCAPACITY는, IllegalArgumentException 예외 미만 0보다 큰 경우, 증설 확인해야 동적 버퍼의 용량 확장 방법을 호출한다.
    원래보다 새로운 확장 용량 후 새로 확장 용량의 바이트 배열 버퍼 용량을 만드는 경우, 검사를 통과 한 후, 후 이동 새로운 배열 이전 데이터를 복사 메모리 복사본 System.arraycopy에 전화 다음 setArray 배열로 대체. 원본보기 tmpNioBuffer 동적으로 확산 후 제어 할 필요가있다.
    새로운 현재의 버퍼 용량은 용량 미만이면, 동적 확장이 요구되지 않고, 필요 서브 버퍼의 일부로서 데이터를 가로채는.
    • 그것은 계속 writerIndex newCapacity 비해보다 작은 경우, 우선, 전류 newCapacity readerIndex 미만, newCapacity writerIndex 단어 이상, 이는 인덱스 데이터의 갱신 완료 후 전류 - 판독 가능 메모리를 복사 newCapacity writerIndex 그것을 System.arraycopy에 설정 될 경우 새로운 바이트 배열 버퍼.
    • newCapacity 적은 readerIndex 이상, 그것은 새로운 읽을 바이트 배열 버퍼에 복사 할 새로운 데이터가없는 것을 나타냅니다, 그냥 newCapacity 마침내 setArray 교체 바이트 배열을 호출 할 수 있습니다 업데이트 readerIndex와 writerIndex을 넣어.
 public ByteBuf capacity(int newCapacity) {
    ensureAccessible();
    if (newCapacity < 0 || newCapacity > maxCapacity()) {
        throw new IllegalArgumentException("newCapacity: " + newCapacity);
    }

    int oldCapacity = array.length;
    if (newCapacity > oldCapacity) {
        byte[] newArray = new byte[newCapacity];
        System.arraycopy(array, 0, newArray, 0, array.length);
        setArray(newArray);
    } else if (newCapacity < oldCapacity) {
        byte[] newArray = new byte[newCapacity];
        int readerIndex = readerIndex();
        if (readerIndex < newCapacity) {
            int writerIndex = writerIndex();
            if (writerIndex > newCapacity) {
                writerIndex(writerIndex = newCapacity);
            }
            System.arraycopy(array, readerIndex, newArray, readerIndex, writerIndex - readerIndex);
        } else {
            setIndex(newCapacity, newCapacity);
        }
        setArray(newArray);
    }
    return this;
}
  • setBytes
    복사 할 바이트의 배열은, 데이터가 제 1 유효 시험, 또는 지수의 값이 0보다 작은 srcIndex 경우 IllegalArgumentException가 인덱스 + 용량 값 + srcIndex 단어 src.length의 값보다 큰 길이 또는 길이의 값보다 큰 경우에 슬로우 , IndexOutOfBoundsException가 발생합니다. 검사를 통과 한 후, System.arraycopy에 바이트 배열의 복제를 호출합니다.
public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {
    checkSrcIndex(index, length, srcIndex, src.length);
    System.arraycopy(src, srcIndex, array, index, length);
    return this;
}
protected final void checkSrcIndex(int index, int length, int srcIndex, int srcCapacity) {
    checkIndex(index, length);
    if (srcIndex < 0 || srcIndex > srcCapacity - length) {
        throw new IndexOutOfBoundsException(String.format(
                "srcIndex: %d, length: %d (expected: range(0, %d))", srcIndex, length, srcCapacity));
    }
}
  • 그물코 ByteBuf NiO 중의 ByteBuffer 변환
    그물코에 ByteBuf가의 ByteBuffer는 정적 메소드 포장에만 ByteBuffer를로 생성하는 대응하는 바이트 배열을 전달할 필요 니오의 ByteBuffer로 변환 nioBuffer 방법 또한 슬라이스 메소드를 호출하여 원래 버퍼의 원래의 ByteBuffer 버퍼 개시 위치의 동일한주기에서 공유 데이터 요소를 생성 할 수있다. 버퍼를 재사용하지 않습니다 nioBuffer 방법은 readerIndex와 writerIndex의 독립성을 보장 할 수 있습니다.
public ByteBuffer nioBuffer(int index, int length) {
    ensureAccessible();
    return ByteBuffer.wrap(array, index, length).slice();
}

PooledByteBuf

메모리 풀 관리를 통해 메모리 풀 관리 Netty4을 추가 한 후 크게 ByteBuf의 성능을 만들기 위해 이전보다 향상되었습니다.

  • PoolChunk
    • 메모리 블록의 최소 단위는 페이지를 할당하는데 사용될 수있다
    • 의 청크 페이지 모음

청크의 메모리 블록을 할당 및 해제를 담당 PoolChunk가 디폴트 페이지 크기가 8K이며, 청크의 크기는 2 ^ 11 페이지, 즉 1,600이고, 바이너리 층 (11)을 구성하는 진 페이지로 구성되며, 하단 리프 노드 8192의 층은 페이지의 숫자로, 각 메모리 할당은 쉽게 메모리 작업의 연속성을 보장해야한다. 메모리 영역의 각 노드는 노드가 메모리 영역 할당을 나타내는 경우, 노드가 할당하여, 노드 메모리 요청의 모든 자식 노드가 무시됩니다 표시됩니다, 자신의 오프셋 주소를 기록합니다. 각각의 메모리 할당은 8K (2 ^ n)은 메모리 크기가 제 층 K의 왼쪽에서 찾고 시작되며, 이용 가능한 메모리 세그먼트를 찾기 위해, 단부 여기서, ChunkSize / (2 ^ k)에 할당해야하는 메모리 블록의 크기 사용 가능한 노드.

  • PoolArena

메모리 할당에있어서, 중앙에 메모리 할당을 관리하는 메모리 할당을 제공하면서, 분리 성능을 해제하기 위해, 일반적으로 메모리의 연속 청크에 미리 할당되며, 상기 메모리 동작이 자주 반복 큰 것으로 할 필요가 없다 연속 메모리 블록 메모리 경기장을이라고하고, PoolArena의 Netty 메모리 풀 구현 클래스입니다.
그물코에 PoolArena 청크는 복수로 구성되며, 각각의 복수 페이지로 이루어지는 청크. PoolArena 공동 청크 및 페이지 및 관리에 의해 구성되어있다.

  • PoolSubpage

각 페이지가 결정되는 제 1 메모리 할당 요청에 따라 메모리 블록 크기의 크기가 동일한 크기의 메모리 블록에 대한 메모리 할당 페이지 미만 나누었을 때의 경우. 당신은 단지 새로운 페이지에서 메모리 할당을 적용 할 수있는 등 메모리 블록의 크기를 적용 할 싶어하지 않으려면 메모리 블록의 크기와 동일 단지, 메모리의 메모리 블록의 첫 번째 메모리 페이지를 할당 할 수 있습니다.
기억 영역 페이지의 사용은 각각의 비트는 하나 개의 점유 영역을 나타내고, 비트 맵을 유지하는 긴 배열을한다.

PooledDirectByteBuf

  • byte 버퍼를 작성
    메모리 풀 구현, 생성 할 때마다 바이트 버퍼, 새로운 직접 때문이 아니라,하지만 메모리 풀에서 얻을, 다음 읽기와 참조 카운터를 설정하고 색인을 쓰기 버퍼의 최대 용량으로 돌아왔다.
static PooledHeapByteBuf newInstance(int maxCapacity) {
    PooledHeapByteBuf buf = RECYCLER.get();
    buf.reuse(maxCapacity);
    return buf;
}
final void reuse(int maxCapacity) {
    maxCapacity(maxCapacity);
    setRefCnt(1);
    setIndex0(0, 0);
    discardMarks();
}
  • 예 바이트 버퍼 복사
    복사 방법 독립적 원래 버퍼, 바이트 버퍼 인스턴스를 복사 할 수있다.
    먼저 인덱스 및 길이의 적법성을 결정하고 새로운 버퍼를 할당 directBuffer PooledByteBufAllocator의 메소드를 호출한다. 다른 서브 클래스가 다른 구현을위한 newDirectBuffer 방법은 추상적 인 방법이다. 다음 풀링되지 않은 경우, 직접, 새로운 버퍼를 생성한다이 풀링 된 경우, 그것은 메모리 풀에서 사용 가능한 다음 버퍼를 얻을 것이다.
public ByteBuf copy(int index, int length) {
    checkIndex(index, length);
    ByteBuf copy = alloc().directBuffer(length, maxCapacity());
    copy.writeBytes(this, index, length);
    return copy;
}
public ByteBuf directBuffer(int initialCapacity, int maxCapacity) {
    if (initialCapacity == 0 && maxCapacity == 0) {
        return emptyBuf;
    }
    validate(initialCapacity, maxCapacity);
    return newDirectBuffer(initialCapacity, maxCapacity);
}
// PooledByteBufAllocator 
protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
    PoolThreadCache cache = threadCache.get();
    PoolArena<ByteBuffer> directArena = cache.directArena;

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

    return toLeakAwareBuffer(buf);
}
//UnpooledByteBufAllocator
protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
    ByteBuf buf;
    if (PlatformDependent.hasUnsafe()) {
        buf = new UnpooledUnsafeDirectByteBuf(this, initialCapacity, maxCapacity);
    } else {
        buf = new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
    }

    return toLeakAwareBuffer(buf);
}

ByteBuf 헬퍼 클래스 분석

ByteBufHolder

ByteBufHolder 다른 프로토콜, 데이터 포맷 및 필드 다른 프로토콜 메시지를 이용하여 데이터 전송이 될 ByteBuf 동일한 때문에 추상화 ByteBufHolder하지 포함하면보다 쉽게 ​​데이터 ByteBuf가 액세스 할 수있는 용기 ByteBuf 인 포장은, 다른 구현이 다른 서브 클래스 사용자는 자신의 필요에 따라 구현할 수 있습니다. 의 Netty는 기본 구현 DefaultByteBufHolder을 제공합니다.

ByteBufAllocator

다른 구현의 Netty 바이트 버퍼에 따른 ByteBufAllocator 바이트 버퍼 분배기는 두 개의 다른 디스펜서 PooledByteBufAllocator 및 UnpooledByteBufAllocator으로 분할된다. 그들은 ByteBuf의 다른 할당 방법을 제공합니다.

CompositeByteBuf

CompositeByteBuf IT는 여러 ByteBuf ByteBuf보기로 조립 될 수 있고, 가상 버퍼이다.
자바 NIO, 우리는 달성하기 위해 두 가지 방법이

  • 에서의 ByteBuffer의 ByteBuffer에 추가 데이터를 복사하거나에서 새로 만든의 ByteBuffer에 새의 ByteBuffer, ByteBuffer를 다른 복사본을 다시 만들어야합니다.
  • 함께 다수의 ByteBuffer, 통합 관리 및 유지 보수에 의한 컨테이너 저장.

에서의 Netty에서 CompositeByByteBuf는 컴포넌트 타입의 컬렉션을 유지 하였다. 컴포넌트 ByteBuf 래퍼 클래스는 중합 ByteBuf된다. 유지 관리 위치 어긋남 량을 수집 정보. 정상적인 상황에서, 우리는 CompositeByteBuf 개체를 인스턴스화하는 생성자가 직접 CompositeByteBuf를 생성하기보다는 ByteBufAllocator.compositeBuffer ()와 Unpooled.wrappedBuffer (ByteBuf ...) 방법을 사용한다.

private int addComponent0(int cIndex, ByteBuf buffer) {
    checkComponentIndex(cIndex);
    if (buffer == null) {
        throw new NullPointerException("buffer");
    }

    int readableBytes = buffer.readableBytes();

    // No need to consolidate - just add a component to the list.
    Component c = new Component(buffer.order(ByteOrder.BIG_ENDIAN).slice());
    if (cIndex == components.size()) {
        components.add(c);
        if (cIndex == 0) {
            c.endOffset = readableBytes;
        } else {
            Component prev = components.get(cIndex - 1);
            c.offset = prev.endOffset;
            c.endOffset = c.offset + readableBytes;
        }
    } else {
        components.add(cIndex, c);
        if (readableBytes != 0) {
            updateComponentOffsets(cIndex);
        }
    }
    return cIndex;
}
private void consolidateIfNeeded() {
    final int numComponents = components.size();
    if (numComponents > maxNumComponents) {
        final int capacity = components.get(numComponents - 1).endOffset;
    
        ByteBuf consolidated = allocBuffer(capacity);
    
        for (int i = 0; i < numComponents; i ++) {
            Component c = components.get(i);
            ByteBuf b = c.buf;
            consolidated.writeBytes(b);
            c.freeIfNecessary();
        }
        Component c = new Component(consolidated);
        c.endOffset = c.length;
        components.clear();
        components.add(c);
    }
}

public CompositeByteBuf removeComponent(int cIndex) {
    checkComponentIndex(cIndex);
    Component comp = components.remove(cIndex);
    comp.freeIfNecessary();
    if (comp.length > 0) {
        updateComponentOffsets(cIndex);
    }
    return this;
}

private static final class Component {
    final ByteBuf buf;
    final int length;
    int offset;
    int endOffset;

    Component(ByteBuf buf) {
        this.buf = buf;
        length = buf.readableBytes();
    }

    void freeIfNecessary() {
        buf.release(); // We should not get a NPE here. If so, it must be a bug.
    }
}

ByteBufUtil

ByteBufUtil는 ByteBuf를 조작하는 일련의 정적 메소드를 제공 유틸리티 클래스 ByteBuf이다.

추천

출처blog.csdn.net/qq_34730511/article/details/80568062