给出以下代码示例:
try (AutoClosable closable = new XXX()) {
o.method1(closable);
o.method2();
}
Java 内存模型是否允许 HotSpot 在 o.method2()
之前重新排序 closable.close()
?
我在这个问题的第一部分故意省略了诸如does method1 captures closable?之类的实现细节。
我的具体用例是这样的:
我有一个 C 库,可以概括为:
static char* value;
void capture(char* ptr){
value = ptr;
}
int len(void) {
return strlen(value);
}
这个 native 库是使用 JNA 包装的
interface CApi extends Library {
static {
Native.register("test.so", CApi.class)
}
void capture(Pointer s);
int test();
}
我原来的客户端代码看起来像这样:
cApi = Native.loadLibrary("test.so", CApi.class);
byte[] data = Native.toByteArray("foo");
Memory m = new Memory(data.length + 1);
m.write(0, data, 0, data.length);
m.setByte(data.length, (byte)0);
cApi.capture(m);
System.out.print(cApi.len());
第一个版本的问题是 m
是从 Java 分配的,并且该内存的生命周期与 m
的强可达性相关。一旦 capture(m)
就不再能够强烈访问 m
,并且当 GC 启动时,内存将被释放(JNA 依赖 finalize
来释放 native 内存)
为了防止这种情况,我建议对 JNA 的 Memory
进行子类化,以引入 AutoClosableMemory
。这个想法是,使用 try-with-resource 构造将清楚地表明该资源在此范围内是可以访问的。好处是我们可以在不再需要 native 内存时立即释放它,而不必等待 GC(嘿,这就是 RAII!)。
try (AutoClosableMemory m = [...]) {
cApi.capture(m);
cApi.test();
}
我能否保证 m.close()
在 cApi.test()
之前永远不会被调用,并且 m
是强可达的?在这种情况下,m
被底层 C API 捕获,但 java 编译器无法知道它。
最佳答案
理论上,JVM 可以对指令重新排序,但方法的结果必须保持正确。 HotSpot 不理解 native 代码,为了保证正确性,它从不围绕 native 指令重新排序代码。
关于java - JMM 保证 try-with-resource 和 JNI 调用中的重新排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46735264/