编写 Java 代码,使用 class 的两个实现之一进行编译
Writing Java code that compiles using one of two implementations of a class
我正在 Java 中编写一些 FFI 代码,大量使用 sun.misc.Unsafe
。
在Java9中,这个class将变得不可访问,并且会变成jdk.unsupported.Unsafe
。我想编写我的代码以便它现在可以工作,但在 Java 9.
中继续工作
最简单的方法是什么?我更喜欢二进制兼容性,但源兼容性也可以。
编辑:每次调用 Unsafe
上的方法时,我 100% 不同意使用反射——甚至是虚拟分派。这些方法中的大多数 编译为单个机器指令 。因此,性能真的很重要。可以使用包装器 – 但前提是我可以确定 JIT 每次都会内联它们。
我目前的计划是在运行时加载适当的 class。
一种方法是编写一个 class 来包装任一实现并使用辅助方法来获得正确的实现。您可以检查 System.getProperties().getProperty("java.version") 以了解您使用的是 JDK 1.8 还是 1.9,或者您可以 try/catch 获取 class就像我一样。
遗憾的是,没有可实现的接口,因此您必须像普通对象一样使用它。您必须包装每个方法。也许有更通用的方法来做到这一点。
示例开始实施
/**
* A wrapper to a sun.misc.Unsafe or jdk.unsupported.Unsafe object.
*/
public class MyUnsafe {
// the implementing class (sun.misc.Unsafe or jdk.unsupported.Unsafe)
private Class<?> unsafeCls;
// an instance of the implementing class
private Object unsafeObj;
// constructor
public MyUnsafe() throws InstantiationException, IllegalAccessException {
unsafeCls = getImplClass();
unsafeObj = unsafeCls.newInstance();
}
// get the implementing class
private Class<?> getImplClass() {
Class<?> implClass = null;
try {
// JDK 1.8 and earlier
implClass = Class.forName("sun.misc.Unsafe");
}
catch (ClassNotFoundException e1) {
try {
// JDK 1.9 and later
implClass = Class.forName("jdk.unsupported.Unsafe");
}
catch (ClassNotFoundException e2) {
// TODO - something that wraps both e1 and e2
throw new RuntimeException(e2);
}
}
return implClass;
}
// Wrap methods
// for example
public Object getObject(Object obj, long offset) throws Exception {
Method mtd = unsafeCls.getMethod("getObject", new Class<?>[] { Object.class, long.class });
return mtd.invoke(unsafeObj, new Object[] { obj, offset });
}
// and so on...
}
一个选项:您可以为需要访问的 Unsafe 上的方法提供一个小的 shim 帮助程序接口,有两种实现:一种用于 sun.misc.Unsafe
,一种用于新的 jdk.unsupported.Unsafe
.两个 classes 都可以作为二进制资源存储在您的 JAR 中,并且在 class 初始化期间(即在 static
块中)您可以创建一个新的 ClassLoader 并加载 shim class。这会在 class 初始化期间给你一些反射开销,但在运行时不涉及反射,只有一个虚拟方法分派——JIT 应该能够内联,只要你只加载一个实现 class.
如果你可以引入一个库依赖,cglib 的 FastClass 基本上会为你做这件事,而不需要很多努力。
像这样的低级性能黑客一样,您需要一些数据。为此创建一个 JMH 测试工具,并验证反射开销确实 是 无法忍受的,并且 JIT 确实可以内联您的解决方案——这是唯一可以确定的方法。
当然,低技术含量的解决方案是简单地记录您的库与 Java 9 不兼容,并发布一个适用于 Java 9 的单独版本分支(但不是在以前的版本上)。这会将问题推给您的客户,但这对于绝大多数实际用例来说可能已经足够好了。
我正在 Java 中编写一些 FFI 代码,大量使用 sun.misc.Unsafe
。
在Java9中,这个class将变得不可访问,并且会变成jdk.unsupported.Unsafe
。我想编写我的代码以便它现在可以工作,但在 Java 9.
最简单的方法是什么?我更喜欢二进制兼容性,但源兼容性也可以。
编辑:每次调用 Unsafe
上的方法时,我 100% 不同意使用反射——甚至是虚拟分派。这些方法中的大多数 编译为单个机器指令 。因此,性能真的很重要。可以使用包装器 – 但前提是我可以确定 JIT 每次都会内联它们。
我目前的计划是在运行时加载适当的 class。
一种方法是编写一个 class 来包装任一实现并使用辅助方法来获得正确的实现。您可以检查 System.getProperties().getProperty("java.version") 以了解您使用的是 JDK 1.8 还是 1.9,或者您可以 try/catch 获取 class就像我一样。
遗憾的是,没有可实现的接口,因此您必须像普通对象一样使用它。您必须包装每个方法。也许有更通用的方法来做到这一点。
示例开始实施
/**
* A wrapper to a sun.misc.Unsafe or jdk.unsupported.Unsafe object.
*/
public class MyUnsafe {
// the implementing class (sun.misc.Unsafe or jdk.unsupported.Unsafe)
private Class<?> unsafeCls;
// an instance of the implementing class
private Object unsafeObj;
// constructor
public MyUnsafe() throws InstantiationException, IllegalAccessException {
unsafeCls = getImplClass();
unsafeObj = unsafeCls.newInstance();
}
// get the implementing class
private Class<?> getImplClass() {
Class<?> implClass = null;
try {
// JDK 1.8 and earlier
implClass = Class.forName("sun.misc.Unsafe");
}
catch (ClassNotFoundException e1) {
try {
// JDK 1.9 and later
implClass = Class.forName("jdk.unsupported.Unsafe");
}
catch (ClassNotFoundException e2) {
// TODO - something that wraps both e1 and e2
throw new RuntimeException(e2);
}
}
return implClass;
}
// Wrap methods
// for example
public Object getObject(Object obj, long offset) throws Exception {
Method mtd = unsafeCls.getMethod("getObject", new Class<?>[] { Object.class, long.class });
return mtd.invoke(unsafeObj, new Object[] { obj, offset });
}
// and so on...
}
一个选项:您可以为需要访问的 Unsafe 上的方法提供一个小的 shim 帮助程序接口,有两种实现:一种用于 sun.misc.Unsafe
,一种用于新的 jdk.unsupported.Unsafe
.两个 classes 都可以作为二进制资源存储在您的 JAR 中,并且在 class 初始化期间(即在 static
块中)您可以创建一个新的 ClassLoader 并加载 shim class。这会在 class 初始化期间给你一些反射开销,但在运行时不涉及反射,只有一个虚拟方法分派——JIT 应该能够内联,只要你只加载一个实现 class.
如果你可以引入一个库依赖,cglib 的 FastClass 基本上会为你做这件事,而不需要很多努力。
像这样的低级性能黑客一样,您需要一些数据。为此创建一个 JMH 测试工具,并验证反射开销确实 是 无法忍受的,并且 JIT 确实可以内联您的解决方案——这是唯一可以确定的方法。
当然,低技术含量的解决方案是简单地记录您的库与 Java 9 不兼容,并发布一个适用于 Java 9 的单独版本分支(但不是在以前的版本上)。这会将问题推给您的客户,但这对于绝大多数实际用例来说可能已经足够好了。