We should be able to use the ByteBuffer cleaner on java8 (and earlier… (#8234)
* We should be able to use the ByteBuffer cleaner on java8 (and earlier versions) even if sun.misc.Unsafe is not present. Motivation: At the moment we have a hard dependency on sun.misc.Unsafe to use the Cleaner on Java8 and earlier. This is not really needed as we can still use pure reflection if sun.misc.Unsafe is not present. Modifications: Refactor Cleaner6 to fallback to pure reflection if sun.misc.Unsafe is not present on system. Result: More timely releasing of direct memory on Java8 and earlier when sun.misc.Unsafe is not present.
This commit is contained in:
parent
4a5b61fc13
commit
38eee409c8
@ -21,6 +21,8 @@ import io.netty.util.internal.logging.InternalLoggerFactory;
|
|||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.security.AccessController;
|
||||||
|
import java.security.PrivilegedAction;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,19 +34,50 @@ import java.nio.ByteBuffer;
|
|||||||
final class CleanerJava6 implements Cleaner {
|
final class CleanerJava6 implements Cleaner {
|
||||||
private static final long CLEANER_FIELD_OFFSET;
|
private static final long CLEANER_FIELD_OFFSET;
|
||||||
private static final Method CLEAN_METHOD;
|
private static final Method CLEAN_METHOD;
|
||||||
|
private static final Field CLEANER_FIELD;
|
||||||
|
|
||||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(CleanerJava6.class);
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(CleanerJava6.class);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
long fieldOffset = -1;
|
long fieldOffset;
|
||||||
Method clean = null;
|
Method clean;
|
||||||
|
Field cleanerField;
|
||||||
Throwable error = null;
|
Throwable error = null;
|
||||||
if (PlatformDependent0.hasUnsafe()) {
|
final ByteBuffer direct = ByteBuffer.allocateDirect(1);
|
||||||
ByteBuffer direct = ByteBuffer.allocateDirect(1);
|
try {
|
||||||
|
Object mayBeCleanerField = AccessController.doPrivileged(new PrivilegedAction<Object>() {
|
||||||
|
@Override
|
||||||
|
public Object run() {
|
||||||
try {
|
try {
|
||||||
Field cleanerField = direct.getClass().getDeclaredField("cleaner");
|
Field cleanerField = direct.getClass().getDeclaredField("cleaner");
|
||||||
|
if (!PlatformDependent.hasUnsafe()) {
|
||||||
|
// We need to make it accessible if we do not use Unsafe as we will access it via
|
||||||
|
// reflection.
|
||||||
|
cleanerField.setAccessible(true);
|
||||||
|
}
|
||||||
|
return cleanerField;
|
||||||
|
} catch (Throwable cause) {
|
||||||
|
return cause;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (mayBeCleanerField instanceof Throwable) {
|
||||||
|
throw (Throwable) mayBeCleanerField;
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanerField = (Field) mayBeCleanerField;
|
||||||
|
|
||||||
|
final Object cleaner;
|
||||||
|
|
||||||
|
// If we have sun.misc.Unsafe we will use it as its faster then using reflection,
|
||||||
|
// otherwise let us try reflection as last resort.
|
||||||
|
if (PlatformDependent.hasUnsafe()) {
|
||||||
fieldOffset = PlatformDependent0.objectFieldOffset(cleanerField);
|
fieldOffset = PlatformDependent0.objectFieldOffset(cleanerField);
|
||||||
Object cleaner = PlatformDependent0.getObject(direct, fieldOffset);
|
cleaner = PlatformDependent0.getObject(direct, fieldOffset);
|
||||||
|
} else {
|
||||||
|
fieldOffset = -1;
|
||||||
|
cleaner = cleanerField.get(direct);
|
||||||
|
}
|
||||||
clean = cleaner.getClass().getDeclaredMethod("clean");
|
clean = cleaner.getClass().getDeclaredMethod("clean");
|
||||||
clean.invoke(cleaner);
|
clean.invoke(cleaner);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
@ -52,21 +85,21 @@ final class CleanerJava6 implements Cleaner {
|
|||||||
fieldOffset = -1;
|
fieldOffset = -1;
|
||||||
clean = null;
|
clean = null;
|
||||||
error = t;
|
error = t;
|
||||||
|
cleanerField = null;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
error = new UnsupportedOperationException("sun.misc.Unsafe unavailable");
|
|
||||||
}
|
|
||||||
if (error == null) {
|
if (error == null) {
|
||||||
logger.debug("java.nio.ByteBuffer.cleaner(): available");
|
logger.debug("java.nio.ByteBuffer.cleaner(): available");
|
||||||
} else {
|
} else {
|
||||||
logger.debug("java.nio.ByteBuffer.cleaner(): unavailable", error);
|
logger.debug("java.nio.ByteBuffer.cleaner(): unavailable", error);
|
||||||
}
|
}
|
||||||
|
CLEANER_FIELD = cleanerField;
|
||||||
CLEANER_FIELD_OFFSET = fieldOffset;
|
CLEANER_FIELD_OFFSET = fieldOffset;
|
||||||
CLEAN_METHOD = clean;
|
CLEAN_METHOD = clean;
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isSupported() {
|
static boolean isSupported() {
|
||||||
return CLEANER_FIELD_OFFSET != -1;
|
return CLEANER_FIELD_OFFSET != -1 || CLEANER_FIELD != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -74,13 +107,45 @@ final class CleanerJava6 implements Cleaner {
|
|||||||
if (!buffer.isDirect()) {
|
if (!buffer.isDirect()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (System.getSecurityManager() == null) {
|
||||||
try {
|
try {
|
||||||
Object cleaner = PlatformDependent0.getObject(buffer, CLEANER_FIELD_OFFSET);
|
freeDirectBuffer0(buffer);
|
||||||
if (cleaner != null) {
|
|
||||||
CLEAN_METHOD.invoke(cleaner);
|
|
||||||
}
|
|
||||||
} catch (Throwable cause) {
|
} catch (Throwable cause) {
|
||||||
PlatformDependent0.throwException(cause);
|
PlatformDependent0.throwException(cause);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
freeDirectBufferPrivileged(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void freeDirectBufferPrivileged(final ByteBuffer buffer) {
|
||||||
|
Throwable cause = AccessController.doPrivileged(new PrivilegedAction<Throwable>() {
|
||||||
|
@Override
|
||||||
|
public Throwable run() {
|
||||||
|
try {
|
||||||
|
freeDirectBuffer0(buffer);
|
||||||
|
return null;
|
||||||
|
} catch (Throwable cause) {
|
||||||
|
return cause;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (cause != null) {
|
||||||
|
PlatformDependent0.throwException(cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void freeDirectBuffer0(ByteBuffer buffer) throws Exception {
|
||||||
|
final Object cleaner;
|
||||||
|
// If CLEANER_FIELD_OFFSET == -1 we need to use reflection to access the cleaner, otherwise we can use
|
||||||
|
// sun.misc.Unsafe.
|
||||||
|
if (CLEANER_FIELD_OFFSET == -1) {
|
||||||
|
cleaner = CLEANER_FIELD.get(buffer);
|
||||||
|
} else {
|
||||||
|
cleaner = PlatformDependent0.getObject(buffer, CLEANER_FIELD_OFFSET);
|
||||||
|
}
|
||||||
|
if (cleaner != null) {
|
||||||
|
CLEAN_METHOD.invoke(cleaner);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,7 +164,7 @@ public final class PlatformDependent {
|
|||||||
|
|
||||||
MAYBE_SUPER_USER = maybeSuperUser0();
|
MAYBE_SUPER_USER = maybeSuperUser0();
|
||||||
|
|
||||||
if (!isAndroid() && hasUnsafe()) {
|
if (!isAndroid()) {
|
||||||
// only direct to method if we are not running on android.
|
// only direct to method if we are not running on android.
|
||||||
// See https://github.com/netty/netty/issues/2604
|
// See https://github.com/netty/netty/issues/2604
|
||||||
if (javaVersion() >= 9) {
|
if (javaVersion() >= 9) {
|
||||||
|
Loading…
Reference in New Issue
Block a user