Netty 직접 메모리 원리 및 응용 분석
일반적으로 시스템 은 시스템 자체 의 안전성 과 건장 성 을 확보 하기 위해 메모리 를 논리 적 으로 커 널 구역 과 사용자 구역 으로 격 리 시 켜 이해 하기 쉽다.사용자 의 행위 가 통제 불 능 성 이 너무 강하 고 너무 많이 노출 되면 각종 신기 한 용법 을 초래 하여 시스템 의 통제 범 위 를 초과 하기 쉽다.물론 어떤 언어 는 메모 리 를 직접 제어 하 는 것 을 지원 합 니 다.예 를 들 어 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 직접 메모리 원리 에 관 한 자 료 는 다른 관련 글 에 주목 하 십시오!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
[ 네티 인 액션 ] 7. EventLoop와 스레딩 모델또한 애플리케이션의 동시성 요건이나 전반적인 복잡성 때문에 프로젝트의 수명주기 동안 다른 스레드 관련 문제가 발생할 수 있다. 1. io.netty.util.concurrent 패키지는 JDK 패키지인 java.ut...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.