Determine the default number of heap/direct arenas of PooledByteBufAllocator conservatively

- Fixes #1445
- Add PlatformDependent.maxDirectMemory()
- Ensure the default number or arenas is decreased if the max memory of the VM is not large enough.
This commit is contained in:
Trustin Lee 2013-06-14 12:14:45 +09:00
parent fe40d4b67f
commit 0da48e7e7f
2 changed files with 83 additions and 4 deletions

View File

@ -29,10 +29,9 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(PooledByteBufAllocator.class);
private static final int DEFAULT_NUM_HEAP_ARENA = Math.max(0, SystemPropertyUtil.getInt(
"io.netty.allocator.numHeapArenas", Runtime.getRuntime().availableProcessors()));
private static final int DEFAULT_NUM_DIRECT_ARENA = Math.max(0, SystemPropertyUtil.getInt(
"io.netty.allocator.numDirectArenas", Runtime.getRuntime().availableProcessors()));
private static final int DEFAULT_NUM_HEAP_ARENA;
private static final int DEFAULT_NUM_DIRECT_ARENA;
private static final int DEFAULT_PAGE_SIZE;
private static final int DEFAULT_MAX_ORDER; // 8192 << 11 = 16 MiB per chunk
@ -60,6 +59,23 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator {
}
DEFAULT_MAX_ORDER = defaultMaxOrder;
// Determine reasonable default for nHeapArena and nDirectArena.
// Assuming each arena has 3 chunks, the pool should not consume more than 50% of max memory.
final Runtime runtime = Runtime.getRuntime();
final int defaultChunkSize = DEFAULT_PAGE_SIZE << DEFAULT_MAX_ORDER;
DEFAULT_NUM_HEAP_ARENA = Math.max(0,
SystemPropertyUtil.getInt(
"io.netty.allocator.numHeapArenas",
(int) Math.min(
runtime.availableProcessors(),
Runtime.getRuntime().maxMemory() / defaultChunkSize / 2 / 3)));
DEFAULT_NUM_DIRECT_ARENA = Math.max(0,
SystemPropertyUtil.getInt(
"io.netty.allocator.numDirectArenas",
(int) Math.min(
runtime.availableProcessors(),
PlatformDependent.maxDirectMemory() / defaultChunkSize / 2 / 3)));
if (logger.isDebugEnabled()) {
logger.debug("io.netty.allocator.numHeapArenas: {}", DEFAULT_NUM_HEAP_ARENA);
logger.debug("io.netty.allocator.numDirectArenas: {}", DEFAULT_NUM_DIRECT_ARENA);

View File

@ -23,15 +23,20 @@ import io.netty.util.internal.logging.InternalLoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -47,6 +52,9 @@ public final class PlatformDependent {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(PlatformDependent.class);
private static final Pattern MAX_DIRECT_MEMORY_SIZE_ARG_PATTERN = Pattern.compile(
"\\s*-XX:MaxDirectMemorySize\\s*=\\s*([0-9]+)\\s*([kKmMgG]?)\\s*$");
private static final boolean IS_ANDROID = isAndroid0();
private static final boolean IS_WINDOWS = isWindows0();
private static final boolean IS_ROOT = isRoot0();
@ -59,6 +67,7 @@ public final class PlatformDependent {
private static final boolean CAN_USE_CHM_V8 = HAS_UNSAFE && JAVA_VERSION < 8;
private static final boolean DIRECT_BUFFER_PREFERRED =
HAS_UNSAFE && !SystemPropertyUtil.getBoolean("io.netty.noPreferDirect", false);
private static final long MAX_DIRECT_MEMORY = maxDirectMemory0();
private static final long ARRAY_BASE_OFFSET = arrayBaseOffset0();
@ -129,6 +138,13 @@ public final class PlatformDependent {
return DIRECT_BUFFER_PREFERRED;
}
/**
* Returns the maximum memory reserved for direct buffer allocation.
*/
public static long maxDirectMemory() {
return MAX_DIRECT_MEMORY;
}
/**
* Returns {@code true} if and only if Javassist is available.
*/
@ -469,6 +485,53 @@ public final class PlatformDependent {
return PlatformDependent0.arrayBaseOffset();
}
private static long maxDirectMemory0() {
long maxDirectMemory = 0;
try {
// Try to get from sun.misc.VM.maxDirectMemory() which should be most accurate.
Class<?> vmClass = Class.forName("sun.misc.VM", true, ClassLoader.getSystemClassLoader());
Method m = vmClass.getDeclaredMethod("maxDirectMemory");
maxDirectMemory = ((Number) m.invoke(null)).longValue();
} catch (Throwable t) {
// Ignore
}
if (maxDirectMemory <= 0) {
// Now try to get the JVM option (-XX:MaxDirectMemorySize) and parse it.
RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
List<String> vmArgs = runtime.getInputArguments();
for (int i = vmArgs.size() - 1; i >= 0; i --) {
Matcher m = MAX_DIRECT_MEMORY_SIZE_ARG_PATTERN.matcher(vmArgs.get(i));
if (!m.matches()) {
continue;
}
maxDirectMemory = Long.parseLong(m.group(1));
switch (m.group(2).charAt(0)) {
case 'k': case 'K':
maxDirectMemory *= 1024;
break;
case 'm': case 'M':
maxDirectMemory *= 1024 * 1024;
break;
case 'g': case 'G':
maxDirectMemory *= 1024 * 1024 * 1024;
break;
}
break;
}
}
if (maxDirectMemory <= 0) {
maxDirectMemory = Runtime.getRuntime().maxMemory();
logger.debug("maxDirectMemory: {} bytes (maybe)", maxDirectMemory);
} else {
logger.debug("maxDirectMemory: {} bytes", maxDirectMemory);
}
return maxDirectMemory;
}
private static boolean hasJavassist0() {
boolean noJavassist = SystemPropertyUtil.getBoolean("io.netty.noJavassist", false);
if (noJavassist) {