From 512d1a11ff2ea041e2fbaddae492c1b053fc9076 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 21 Jul 2014 07:00:46 +0200 Subject: [PATCH] [#2685] Epoll transport should use GetPrimitiveArrayCritical / ReleasePrimitiveArrayCritical Motivation: At the moment we use Get*ArrayElement all the time in the epoll transport which may be wasteful as the JVM may do a memory copy for this. For code-path that will get executed fast (without blocking) we should better make use of GetPrimitiveArrayCritical and ReleasePrimitiveArrayCritical as this signal the JVM that we not want to do any memory copy if not really needed. It is important to only do this on non-blocking code-path as this may even suspend the GC to disallow the JVM to move the arrays around. See also http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#GetPrimitiveArrayCritical Modification: Make use of GetPrimitiveArrayCritical / ReleasePrimitiveArrayCritical as replacement for Get*ArrayElement / Release*ArrayElement where possible. Result: Better performance due less memory copies. --- .../main/c/io_netty_channel_epoll_Native.c | 49 ++++++++++++++----- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c index 2c13f91cc4..c8f18876fa 100644 --- a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c +++ b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c @@ -162,9 +162,17 @@ jobject createDatagramSocketAddress(JNIEnv * env, struct sockaddr_storage addr, return socketAddr; } -void init_sockaddr(JNIEnv * env, jbyteArray address, jint scopeId, jint jport, struct sockaddr_storage * addr) { +int init_sockaddr(JNIEnv * env, jbyteArray address, jint scopeId, jint jport, struct sockaddr_storage * addr) { uint16_t port = htons((uint16_t) jport); - jbyte* addressBytes = (*env)->GetByteArrayElements(env, address, 0); + // Use GetPrimitiveArrayCritical and ReleasePrimitiveArrayCritical to signal the VM that we really would like + // to not do a memory copy here. This is ok as we not do any blocking action here anyway. + // This is important as the VM may suspend GC for the time! + jbyte* addressBytes = (*env)->GetPrimitiveArrayCritical(env, address, 0); + if (addressBytes == NULL) { + // No memory left ?!?!? + throwOutOfMemoryError(env, "Can't allocate memory"); + return -1; + } if (socketType == AF_INET6) { struct sockaddr_in6* ip6addr = (struct sockaddr_in6 *) addr; ip6addr->sin6_family = AF_INET6; @@ -181,7 +189,8 @@ void init_sockaddr(JNIEnv * env, jbyteArray address, jint scopeId, jint jport, s memcpy( &(ipaddr->sin_addr.s_addr), addressBytes + 12, 4); } - (*env)->ReleaseByteArrayElements(env, address, addressBytes, JNI_ABORT); + (*env)->ReleasePrimitiveArrayCritical(env, address, addressBytes, JNI_ABORT); + return 0; } static int socket_type() { @@ -197,14 +206,23 @@ static int socket_type() { } } -void init_in_addr(JNIEnv * env, jbyteArray address, struct in_addr * addr) { - jbyte* addressBytes = (*env)->GetByteArrayElements(env, address, 0); +int init_in_addr(JNIEnv * env, jbyteArray address, struct in_addr * addr) { + // Use GetPrimitiveArrayCritical and ReleasePrimitiveArrayCritical to signal the VM that we really would like + // to not do a memory copy here. This is ok as we not do any blocking action here anyway. + // This is important as the VM may suspend GC for the time! + jbyte* addressBytes = (*env)->GetPrimitiveArrayCritical(env, address, 0); + if (addressBytes == NULL) { + // No memory left ?!?!? + throwOutOfMemoryError(env, "Can't allocate memory"); + return -1; + } if (socketType == AF_INET6) { memcpy(addr, addressBytes, 16); } else { memcpy(addr, addressBytes + 12, 4); } - (*env)->ReleaseByteArrayElements(env, address, addressBytes, JNI_ABORT); + (*env)->ReleasePrimitiveArrayCritical(env, address, addressBytes, JNI_ABORT); + return 0; } // util methods end @@ -496,7 +514,10 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_epollWait(JNIEnv * env } jboolean isCopy; - jlong *elements = (*env)->GetLongArrayElements(env, events, &isCopy); + // Use GetPrimitiveArrayCritical and ReleasePrimitiveArrayCritical to signal the VM that we really would like + // to not do a memory copy here. This is ok as we not do any blocking action here anyway. + // This is important as the VM may suspend GC for the time! + jlong *elements = (*env)->GetPrimitiveArrayCritical(env, events, &isCopy); if (elements == NULL) { // No memory left ?!?!? throwOutOfMemoryError(env, "Can't allocate memory"); @@ -524,7 +545,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_epollWait(JNIEnv * env // was just pinned so use JNI_ABORT to eliminate not needed copy. mode = JNI_ABORT; } - (*env)->ReleaseLongArrayElements(env, events, elements, mode); + (*env)->ReleasePrimitiveArrayCritical(env, events, elements, mode); return ready; } @@ -590,7 +611,9 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_writeAddress(JNIEnv * jint sendTo0(JNIEnv * env, jint fd, void* buffer, jint pos, jint limit ,jbyteArray address, jint scopeId, jint port) { struct sockaddr_storage addr; - init_sockaddr(env, address, scopeId, port, &addr); + if (init_sockaddr(env, address, scopeId, port, &addr) == -1) { + return -1; + } ssize_t res; int err; @@ -847,7 +870,9 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_socketStream(JNIEnv * JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_bind(JNIEnv * env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port) { struct sockaddr_storage addr; - init_sockaddr(env, address, scopeId, port, &addr); + if (init_sockaddr(env, address, scopeId, port, &addr) == -1) { + return -1; + } if(bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1){ int err = errno; @@ -864,7 +889,9 @@ JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_listen(JNIEnv * env, j JNIEXPORT jboolean JNICALL Java_io_netty_channel_epoll_Native_connect(JNIEnv * env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port) { struct sockaddr_storage addr; - init_sockaddr(env, address, scopeId, port, &addr); + if (init_sockaddr(env, address, scopeId, port, &addr) == -1) { + return -1; + } int res; int err;