2dc686ded1
Motivation Direct buffers are normally preferred when interfacing with raw sockets. Currently netty will only return direct io buffers (for reading from a channel) when a platform has unsafe. However, this is inconsistent with the write-side (filterOutboundMessage) where a direct byte buffer will be returned if pooling is enabled. This means that environments without unsafe (and no manual netty configurations) end up with many pooled heap byte buffers for reading, many pooled direct byte buffers for writing, and jdk pooled byte buffers (for reading). Modifications This commit modifies the AbstractByteBufAllocator to return a direct byte buffer for io handling when the platform has unsafe or direct byte buffers are pooled. Result: Use direct buffers when direct buffers are pooled for IO.
284 lines
9.0 KiB
Java
284 lines
9.0 KiB
Java
/*
|
|
* Copyright 2012 The Netty Project
|
|
*
|
|
* The Netty Project licenses this file to you under the Apache License,
|
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
|
|
package io.netty.buffer;
|
|
|
|
import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
|
|
|
|
import io.netty.util.ResourceLeakDetector;
|
|
import io.netty.util.ResourceLeakTracker;
|
|
import io.netty.util.internal.PlatformDependent;
|
|
import io.netty.util.internal.StringUtil;
|
|
|
|
/**
|
|
* Skeletal {@link ByteBufAllocator} implementation to extend.
|
|
*/
|
|
public abstract class AbstractByteBufAllocator implements ByteBufAllocator {
|
|
static final int DEFAULT_INITIAL_CAPACITY = 256;
|
|
static final int DEFAULT_MAX_CAPACITY = Integer.MAX_VALUE;
|
|
static final int DEFAULT_MAX_COMPONENTS = 16;
|
|
static final int CALCULATE_THRESHOLD = 1048576 * 4; // 4 MiB page
|
|
|
|
static {
|
|
ResourceLeakDetector.addExclusions(AbstractByteBufAllocator.class, "toLeakAwareBuffer");
|
|
}
|
|
|
|
protected static ByteBuf toLeakAwareBuffer(ByteBuf buf) {
|
|
ResourceLeakTracker<ByteBuf> leak;
|
|
switch (ResourceLeakDetector.getLevel()) {
|
|
case SIMPLE:
|
|
leak = AbstractByteBuf.leakDetector.track(buf);
|
|
if (leak != null) {
|
|
buf = new SimpleLeakAwareByteBuf(buf, leak);
|
|
}
|
|
break;
|
|
case ADVANCED:
|
|
case PARANOID:
|
|
leak = AbstractByteBuf.leakDetector.track(buf);
|
|
if (leak != null) {
|
|
buf = new AdvancedLeakAwareByteBuf(buf, leak);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
protected static CompositeByteBuf toLeakAwareBuffer(CompositeByteBuf buf) {
|
|
ResourceLeakTracker<ByteBuf> leak;
|
|
switch (ResourceLeakDetector.getLevel()) {
|
|
case SIMPLE:
|
|
leak = AbstractByteBuf.leakDetector.track(buf);
|
|
if (leak != null) {
|
|
buf = new SimpleLeakAwareCompositeByteBuf(buf, leak);
|
|
}
|
|
break;
|
|
case ADVANCED:
|
|
case PARANOID:
|
|
leak = AbstractByteBuf.leakDetector.track(buf);
|
|
if (leak != null) {
|
|
buf = new AdvancedLeakAwareCompositeByteBuf(buf, leak);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
private final boolean directByDefault;
|
|
private final ByteBuf emptyBuf;
|
|
|
|
/**
|
|
* Instance use heap buffers by default
|
|
*/
|
|
protected AbstractByteBufAllocator() {
|
|
this(false);
|
|
}
|
|
|
|
/**
|
|
* Create new instance
|
|
*
|
|
* @param preferDirect {@code true} if {@link #buffer(int)} should try to allocate a direct buffer rather than
|
|
* a heap buffer
|
|
*/
|
|
protected AbstractByteBufAllocator(boolean preferDirect) {
|
|
directByDefault = preferDirect && PlatformDependent.hasUnsafe();
|
|
emptyBuf = new EmptyByteBuf(this);
|
|
}
|
|
|
|
@Override
|
|
public ByteBuf buffer() {
|
|
if (directByDefault) {
|
|
return directBuffer();
|
|
}
|
|
return heapBuffer();
|
|
}
|
|
|
|
@Override
|
|
public ByteBuf buffer(int initialCapacity) {
|
|
if (directByDefault) {
|
|
return directBuffer(initialCapacity);
|
|
}
|
|
return heapBuffer(initialCapacity);
|
|
}
|
|
|
|
@Override
|
|
public ByteBuf buffer(int initialCapacity, int maxCapacity) {
|
|
if (directByDefault) {
|
|
return directBuffer(initialCapacity, maxCapacity);
|
|
}
|
|
return heapBuffer(initialCapacity, maxCapacity);
|
|
}
|
|
|
|
@Override
|
|
public ByteBuf ioBuffer() {
|
|
if (PlatformDependent.hasUnsafe() || isDirectBufferPooled()) {
|
|
return directBuffer(DEFAULT_INITIAL_CAPACITY);
|
|
}
|
|
return heapBuffer(DEFAULT_INITIAL_CAPACITY);
|
|
}
|
|
|
|
@Override
|
|
public ByteBuf ioBuffer(int initialCapacity) {
|
|
if (PlatformDependent.hasUnsafe() || isDirectBufferPooled()) {
|
|
return directBuffer(initialCapacity);
|
|
}
|
|
return heapBuffer(initialCapacity);
|
|
}
|
|
|
|
@Override
|
|
public ByteBuf ioBuffer(int initialCapacity, int maxCapacity) {
|
|
if (PlatformDependent.hasUnsafe() || isDirectBufferPooled()) {
|
|
return directBuffer(initialCapacity, maxCapacity);
|
|
}
|
|
return heapBuffer(initialCapacity, maxCapacity);
|
|
}
|
|
|
|
@Override
|
|
public ByteBuf heapBuffer() {
|
|
return heapBuffer(DEFAULT_INITIAL_CAPACITY, DEFAULT_MAX_CAPACITY);
|
|
}
|
|
|
|
@Override
|
|
public ByteBuf heapBuffer(int initialCapacity) {
|
|
return heapBuffer(initialCapacity, DEFAULT_MAX_CAPACITY);
|
|
}
|
|
|
|
@Override
|
|
public ByteBuf heapBuffer(int initialCapacity, int maxCapacity) {
|
|
if (initialCapacity == 0 && maxCapacity == 0) {
|
|
return emptyBuf;
|
|
}
|
|
validate(initialCapacity, maxCapacity);
|
|
return newHeapBuffer(initialCapacity, maxCapacity);
|
|
}
|
|
|
|
@Override
|
|
public ByteBuf directBuffer() {
|
|
return directBuffer(DEFAULT_INITIAL_CAPACITY, DEFAULT_MAX_CAPACITY);
|
|
}
|
|
|
|
@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);
|
|
}
|
|
|
|
@Override
|
|
public CompositeByteBuf compositeBuffer() {
|
|
if (directByDefault) {
|
|
return compositeDirectBuffer();
|
|
}
|
|
return compositeHeapBuffer();
|
|
}
|
|
|
|
@Override
|
|
public CompositeByteBuf compositeBuffer(int maxNumComponents) {
|
|
if (directByDefault) {
|
|
return compositeDirectBuffer(maxNumComponents);
|
|
}
|
|
return compositeHeapBuffer(maxNumComponents);
|
|
}
|
|
|
|
@Override
|
|
public CompositeByteBuf compositeHeapBuffer() {
|
|
return compositeHeapBuffer(DEFAULT_MAX_COMPONENTS);
|
|
}
|
|
|
|
@Override
|
|
public CompositeByteBuf compositeHeapBuffer(int maxNumComponents) {
|
|
return toLeakAwareBuffer(new CompositeByteBuf(this, false, maxNumComponents));
|
|
}
|
|
|
|
@Override
|
|
public CompositeByteBuf compositeDirectBuffer() {
|
|
return compositeDirectBuffer(DEFAULT_MAX_COMPONENTS);
|
|
}
|
|
|
|
@Override
|
|
public CompositeByteBuf compositeDirectBuffer(int maxNumComponents) {
|
|
return toLeakAwareBuffer(new CompositeByteBuf(this, true, maxNumComponents));
|
|
}
|
|
|
|
private static void validate(int initialCapacity, int maxCapacity) {
|
|
checkPositiveOrZero(initialCapacity, "initialCapacity");
|
|
if (initialCapacity > maxCapacity) {
|
|
throw new IllegalArgumentException(String.format(
|
|
"initialCapacity: %d (expected: not greater than maxCapacity(%d)",
|
|
initialCapacity, maxCapacity));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a heap {@link ByteBuf} with the given initialCapacity and maxCapacity.
|
|
*/
|
|
protected abstract ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity);
|
|
|
|
/**
|
|
* Create a direct {@link ByteBuf} with the given initialCapacity and maxCapacity.
|
|
*/
|
|
protected abstract ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity);
|
|
|
|
@Override
|
|
public String toString() {
|
|
return StringUtil.simpleClassName(this) + "(directByDefault: " + directByDefault + ')';
|
|
}
|
|
|
|
@Override
|
|
public int calculateNewCapacity(int minNewCapacity, int maxCapacity) {
|
|
checkPositiveOrZero(minNewCapacity, "minNewCapacity");
|
|
if (minNewCapacity > maxCapacity) {
|
|
throw new IllegalArgumentException(String.format(
|
|
"minNewCapacity: %d (expected: not greater than maxCapacity(%d)",
|
|
minNewCapacity, maxCapacity));
|
|
}
|
|
final int threshold = CALCULATE_THRESHOLD; // 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);
|
|
}
|
|
}
|